dd-trace 6.3.0 → 6.5.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
@@ -6,7 +6,10 @@ const path = require('node:path')
6
6
  const log = require('../log')
7
7
  const { getNumFromKnownTests } = require('../plugins/util/test')
8
8
  const { parseKnownTestsResponse } = require('./early-flake-detection/get-known-tests')
9
- const { parseSkippableSuitesResponse } = require('./intelligent-test-runner/get-skippable-suites')
9
+ const {
10
+ logSkippableSuitesResponse,
11
+ parseSkippableSuitesResponse,
12
+ } = require('./intelligent-test-runner/get-skippable-suites')
10
13
  const { parseLibraryConfigurationResponse } = require('./requests/get-library-configuration')
11
14
  const {
12
15
  getNumFromTestManagementTests,
@@ -45,6 +48,10 @@ const SKIPPABLE_TESTS_FILE_NAME = 'skippable_tests.json'
45
48
  const TEST_MANAGEMENT_FILE_NAME = 'test_management.json'
46
49
 
47
50
  const RUNFILES_MANIFEST_SEPARATOR = ' '
51
+ const DEFAULT_VALIDATION_MAX_FILE_BYTES = 1024 * 1024
52
+ const DEFAULT_VALIDATION_MAX_ENTRIES = 100_000
53
+ const DEFAULT_VALIDATION_MAX_NESTING_DEPTH = 32
54
+ const DEFAULT_VALIDATION_MAX_STRING_BYTES = 4096
48
55
 
49
56
  function parseManifestVersion (content) {
50
57
  // Supported just the number version or 'version=x'
@@ -54,12 +61,22 @@ function parseManifestVersion (content) {
54
61
  }
55
62
 
56
63
  class TestOptimizationHttpCache {
57
- constructor ({ cwd = process.cwd(), env } = {}) {
64
+ constructor ({
65
+ cwd = process.cwd(),
66
+ env,
67
+ validationManifestPath,
68
+ maxFileBytes = DEFAULT_VALIDATION_MAX_FILE_BYTES,
69
+ } = {}) {
58
70
  this._cwd = cwd
59
71
  // This cache intentionally consumes env vars that are not tracer config keys. (??)
60
72
  // eslint-disable-next-line eslint-rules/eslint-process-env
61
73
  this._env = env ?? process.env
62
- this._manifestPath = this._resolveManifestPath()
74
+ this._validationManifestPath = validationManifestPath
75
+ this._maxFileBytes = maxFileBytes
76
+ this._lastError = undefined
77
+ this._manifestPath = validationManifestPath
78
+ ? this._resolveValidationManifestPath(validationManifestPath)
79
+ : this._resolveManifestPath()
63
80
  this._testOptimizationPath = undefined
64
81
  this._httpCachePath = undefined
65
82
  this._available = false
@@ -71,6 +88,10 @@ class TestOptimizationHttpCache {
71
88
  return this._available
72
89
  }
73
90
 
91
+ getLastError () {
92
+ return this._lastError
93
+ }
94
+
74
95
  readSettings () {
75
96
  const payload = this._readFile(SETTINGS_FILE_NAME)
76
97
  if (payload === CACHE_MISS) {
@@ -79,7 +100,11 @@ class TestOptimizationHttpCache {
79
100
  }
80
101
 
81
102
  try {
82
- const settings = parseLibraryConfigurationResponse(payload, undefined, { validateRequiredFields: true })
103
+ const settings = parseLibraryConfigurationResponse(
104
+ this._parsePayload(payload, SETTINGS_FILE_NAME),
105
+ undefined,
106
+ { validateRequiredFields: true, validationMode: Boolean(this._validationManifestPath) }
107
+ )
83
108
  incrementCountMetric(TELEMETRY_GIT_REQUESTS_SETTINGS_RESPONSE, settings)
84
109
  return settings
85
110
  } catch (err) {
@@ -94,7 +119,10 @@ class TestOptimizationHttpCache {
94
119
  if (payload === CACHE_MISS) return CACHE_MISS
95
120
 
96
121
  try {
97
- const knownTests = parseKnownTestsResponse(payload, { validateRequiredFields: true })
122
+ const knownTests = parseKnownTestsResponse(
123
+ this._parsePayload(payload, KNOWN_TESTS_FILE_NAME),
124
+ { validateRequiredFields: true, validationMode: Boolean(this._validationManifestPath) }
125
+ )
98
126
  distributionMetric(TELEMETRY_KNOWN_TESTS_RESPONSE_TESTS, {}, getNumFromKnownTests(knownTests))
99
127
  distributionMetric(TELEMETRY_KNOWN_TESTS_RESPONSE_BYTES, {}, payload.length)
100
128
  return knownTests
@@ -109,9 +137,15 @@ class TestOptimizationHttpCache {
109
137
  if (payload === CACHE_MISS) return CACHE_MISS
110
138
 
111
139
  try {
112
- const parsedResponse = JSON.parse(payload)
113
- const result = parseSkippableSuitesResponse(parsedResponse, { ...options, validateRequiredFields: true })
140
+ const boundedPayload = this._parsePayload(payload, SKIPPABLE_TESTS_FILE_NAME)
141
+ const parsedResponse = typeof boundedPayload === 'string' ? JSON.parse(boundedPayload) : boundedPayload
142
+ const result = parseSkippableSuitesResponse(parsedResponse, {
143
+ ...options,
144
+ validateRequiredFields: true,
145
+ validationMode: Boolean(this._validationManifestPath),
146
+ })
114
147
  const testLevel = options.testLevel || 'suite'
148
+ logSkippableSuitesResponse(result, testLevel)
115
149
  const skippableItems = parsedResponse.data.filter(({ type }) => type === testLevel)
116
150
  incrementCountMetric(
117
151
  testLevel === 'test'
@@ -133,7 +167,10 @@ class TestOptimizationHttpCache {
133
167
  if (payload === CACHE_MISS) return false
134
168
 
135
169
  try {
136
- parseSkippableSuitesResponse(JSON.parse(payload), { ...options, validateRequiredFields: true })
170
+ parseSkippableSuitesResponse(
171
+ this._parsePayload(payload, SKIPPABLE_TESTS_FILE_NAME),
172
+ { ...options, validateRequiredFields: true, validationMode: Boolean(this._validationManifestPath) }
173
+ )
137
174
  return true
138
175
  } catch {
139
176
  return false
@@ -145,7 +182,10 @@ class TestOptimizationHttpCache {
145
182
  if (payload === CACHE_MISS) return CACHE_MISS
146
183
 
147
184
  try {
148
- const testManagementTests = parseTestManagementTestsResponse(payload, { validateRequiredFields: true })
185
+ const testManagementTests = parseTestManagementTestsResponse(
186
+ this._parsePayload(payload, TEST_MANAGEMENT_FILE_NAME),
187
+ { validateRequiredFields: true, validationMode: Boolean(this._validationManifestPath) }
188
+ )
149
189
  distributionMetric(
150
190
  TELEMETRY_TEST_MANAGEMENT_TESTS_RESPONSE_TESTS,
151
191
  {},
@@ -161,12 +201,18 @@ class TestOptimizationHttpCache {
161
201
 
162
202
  _buildReader () {
163
203
  if (!this._manifestPath) {
204
+ if (this._validationManifestPath && !this._lastError) {
205
+ this._setError('Offline Test Optimization validation manifest was not found.')
206
+ }
164
207
  log.debug('Test Optimization HTTP cache manifest not found')
165
208
  return
166
209
  }
167
210
 
168
211
  const version = this._readManifestVersion(this._manifestPath)
169
212
  if (version !== SUPPORTED_MANIFEST_VERSION) {
213
+ if (this._validationManifestPath) {
214
+ this._setError(`Unsupported offline Test Optimization validation manifest version: ${version || 'missing'}.`)
215
+ }
170
216
  log.debug('Unsupported Test Optimization HTTP cache manifest version %j at %s', version, this._manifestPath)
171
217
  return
172
218
  }
@@ -180,6 +226,9 @@ class TestOptimizationHttpCache {
180
226
 
181
227
  const settingsPath = path.join(this._httpCachePath, SETTINGS_FILE_NAME)
182
228
  if (!fs.existsSync(settingsPath)) {
229
+ if (this._validationManifestPath) {
230
+ this._setError('Offline Test Optimization validation settings fixture is missing.')
231
+ }
183
232
  log.debug('Test Optimization HTTP cache settings file not found at %s', settingsPath)
184
233
  return
185
234
  }
@@ -203,6 +252,30 @@ class TestOptimizationHttpCache {
203
252
  }
204
253
  }
205
254
 
255
+ _resolveValidationManifestPath (manifestPath) {
256
+ if (!path.isAbsolute(manifestPath)) {
257
+ this._setError('Offline Test Optimization validation manifest path must be absolute.')
258
+ return
259
+ }
260
+
261
+ const resolvedManifestPath = path.resolve(manifestPath)
262
+ const testOptimizationPath = path.dirname(resolvedManifestPath)
263
+ const fixtureRoot = path.dirname(testOptimizationPath)
264
+ if (path.basename(resolvedManifestPath) !== MANIFEST_FILE_NAME ||
265
+ path.basename(testOptimizationPath) !== PLAN_FOLDER) {
266
+ this._setError('Offline Test Optimization validation manifest must use the fixed .testoptimization layout.')
267
+ return
268
+ }
269
+
270
+ try {
271
+ assertPathComponentsAreNotSymlinks(fixtureRoot, resolvedManifestPath)
272
+ assertRegularFixtureFile(resolvedManifestPath)
273
+ return resolvedManifestPath
274
+ } catch (err) {
275
+ this._setError(err.message)
276
+ }
277
+ }
278
+
206
279
  _resolveRunfilePath (manifestFile) {
207
280
  if (fs.existsSync(manifestFile)) {
208
281
  return manifestFile
@@ -253,8 +326,10 @@ class TestOptimizationHttpCache {
253
326
 
254
327
  _readManifestVersion (manifestPath) {
255
328
  try {
329
+ this._assertValidationFixtureFile(manifestPath)
256
330
  return parseManifestVersion(fs.readFileSync(manifestPath, 'utf8'))
257
331
  } catch (err) {
332
+ if (this._validationManifestPath) this._setError(err.message)
258
333
  log.debug('Failed to read Test Optimization HTTP cache manifest %s: %s', manifestPath, err.message)
259
334
  }
260
335
  }
@@ -264,24 +339,113 @@ class TestOptimizationHttpCache {
264
339
 
265
340
  const filePath = path.join(this._httpCachePath, fileName)
266
341
  try {
342
+ this._assertValidationFixtureFile(filePath)
267
343
  log.debug('Reading Test Optimization HTTP cache file %s', filePath)
268
344
  return fs.readFileSync(filePath, 'utf8')
269
345
  } catch (err) {
346
+ if (this._validationManifestPath) this._setError(err.message)
270
347
  log.debug('Test Optimization HTTP cache file %s could not be read: %s', filePath, err.message)
271
348
  return CACHE_MISS
272
349
  }
273
350
  }
274
351
 
275
352
  _logInvalidCacheFile (fileName, err) {
353
+ if (this._validationManifestPath) {
354
+ this._setError(`Invalid offline Test Optimization ${fileName} fixture: ${err.message}`)
355
+ }
276
356
  log.debug('Test Optimization HTTP cache file %s could not be parsed: %s', fileName, err.message)
277
357
  }
278
358
 
359
+ _assertValidationFixtureFile (filePath) {
360
+ if (!this._validationManifestPath) return
361
+ const fixtureRoot = path.dirname(path.dirname(this._manifestPath))
362
+ assertPathComponentsAreNotSymlinks(fixtureRoot, filePath)
363
+ const stat = assertRegularFixtureFile(filePath)
364
+ if (stat.size > this._maxFileBytes) {
365
+ throw new Error(
366
+ `Offline Test Optimization fixture ${path.basename(filePath)} exceeds ${this._maxFileBytes} bytes.`
367
+ )
368
+ }
369
+ }
370
+
371
+ _parsePayload (payload, fileName) {
372
+ if (!this._validationManifestPath) return payload
373
+
374
+ const parsed = JSON.parse(payload)
375
+ assertBoundedValidationValue(parsed, fileName)
376
+ return parsed
377
+ }
378
+
379
+ _setError (message) {
380
+ this._lastError = new Error(message)
381
+ }
382
+
279
383
  _disable () {
280
384
  this._available = false
281
385
  }
282
386
  }
283
387
 
388
+ function assertPathComponentsAreNotSymlinks (root, filename) {
389
+ const resolvedRoot = path.resolve(root)
390
+ const resolvedFilename = path.resolve(filename)
391
+ const rootStat = fs.lstatSync(resolvedRoot)
392
+ if (!rootStat.isDirectory() || rootStat.isSymbolicLink()) {
393
+ throw new Error(`Offline Test Optimization fixture root must be a regular directory: ${resolvedRoot}`)
394
+ }
395
+ const relative = path.relative(resolvedRoot, resolvedFilename)
396
+ if (relative.startsWith('..') || path.isAbsolute(relative)) {
397
+ throw new Error('Offline Test Optimization fixture path escapes its validation root.')
398
+ }
399
+
400
+ let current = resolvedRoot
401
+ for (const segment of relative ? relative.split(path.sep) : []) {
402
+ current = path.join(current, segment)
403
+ const stat = fs.lstatSync(current)
404
+ if (stat.isSymbolicLink()) {
405
+ throw new Error(`Offline Test Optimization fixture path contains a symbolic link: ${current}`)
406
+ }
407
+ }
408
+ }
409
+
410
+ function assertRegularFixtureFile (filename) {
411
+ const stat = fs.lstatSync(filename)
412
+ if (!stat.isFile()) {
413
+ throw new Error(`Offline Test Optimization fixture is not a regular file: ${filename}`)
414
+ }
415
+ if (stat.nlink > 1) {
416
+ throw new Error(`Offline Test Optimization fixture must not be hard-linked: ${filename}`)
417
+ }
418
+ return stat
419
+ }
420
+
421
+ function assertBoundedValidationValue (value, fileName) {
422
+ const pending = [{ depth: 0, value }]
423
+ let entries = 0
424
+
425
+ while (pending.length > 0) {
426
+ const current = pending.pop()
427
+ if (current.depth > DEFAULT_VALIDATION_MAX_NESTING_DEPTH) {
428
+ throw new Error(`${fileName} exceeds the validation nesting limit.`)
429
+ }
430
+ if (typeof current.value === 'string' &&
431
+ Buffer.byteLength(current.value) > DEFAULT_VALIDATION_MAX_STRING_BYTES) {
432
+ throw new Error(`${fileName} contains a string that exceeds the validation limit.`)
433
+ }
434
+ if (!current.value || typeof current.value !== 'object') continue
435
+
436
+ const values = Array.isArray(current.value) ? current.value : Object.values(current.value)
437
+ entries += values.length
438
+ if (entries > DEFAULT_VALIDATION_MAX_ENTRIES) {
439
+ throw new Error(`${fileName} exceeds the validation collection-entry limit.`)
440
+ }
441
+ for (const nestedValue of values) {
442
+ pending.push({ depth: current.depth + 1, value: nestedValue })
443
+ }
444
+ }
445
+ }
446
+
284
447
  module.exports = {
285
448
  CACHE_MISS,
449
+ DEFAULT_VALIDATION_MAX_FILE_BYTES,
286
450
  TestOptimizationHttpCache,
287
451
  }
@@ -0,0 +1,90 @@
1
+ 'use strict'
2
+
3
+ const { statSync } = require('node:fs')
4
+
5
+ const {
6
+ TEST_FAILURE_SCREENSHOT_UPLOADED,
7
+ TEST_FAILURE_SCREENSHOT_UPLOAD_ERROR,
8
+ } = require('../plugins/util/test')
9
+
10
+ const dateNow = Date.now
11
+
12
+ const SCREENSHOT_UPLOAD_RESULT_UPLOADED = 'uploaded'
13
+ const SCREENSHOT_UPLOAD_RESULT_ERROR = 'error'
14
+
15
+ /**
16
+ * Resolves a screenshot's capture time in epoch milliseconds.
17
+ *
18
+ * @param {object|string} screenshot - Framework screenshot metadata or its file path
19
+ * @param {string} filePath - Resolved screenshot file path
20
+ * @returns {number} Capture time in epoch milliseconds
21
+ */
22
+ function getScreenshotCapturedAtMs (screenshot, filePath) {
23
+ const takenAt = screenshot !== null && typeof screenshot === 'object' ? screenshot.takenAt : undefined
24
+ if (takenAt) {
25
+ const parsedMs = new Date(takenAt).getTime()
26
+ if (Number.isInteger(parsedMs) && parsedMs > 0) {
27
+ return parsedMs
28
+ }
29
+ }
30
+ try {
31
+ return Math.floor(statSync(filePath).mtimeMs)
32
+ } catch {
33
+ return dateNow()
34
+ }
35
+ }
36
+
37
+ /**
38
+ * Combines screenshot upload results, giving errors precedence over successes.
39
+ *
40
+ * @param {Array<string|undefined>} uploadResults - Per-screenshot upload results
41
+ * @returns {string|undefined} Combined upload result
42
+ */
43
+ function getScreenshotUploadResult (uploadResults) {
44
+ let hasUploaded = false
45
+ for (const uploadResult of uploadResults) {
46
+ if (uploadResult === SCREENSHOT_UPLOAD_RESULT_ERROR) {
47
+ return SCREENSHOT_UPLOAD_RESULT_ERROR
48
+ }
49
+ if (uploadResult === SCREENSHOT_UPLOAD_RESULT_UPLOADED) {
50
+ hasUploaded = true
51
+ }
52
+ }
53
+ return hasUploaded ? SCREENSHOT_UPLOAD_RESULT_UPLOADED : undefined
54
+ }
55
+
56
+ /**
57
+ * Returns the test tag that represents an aggregate screenshot upload outcome.
58
+ *
59
+ * @param {string|undefined} uploadResult - Aggregate screenshot upload result
60
+ * @returns {string|undefined} Screenshot upload result tag
61
+ */
62
+ function getScreenshotUploadTag (uploadResult) {
63
+ if (uploadResult === SCREENSHOT_UPLOAD_RESULT_ERROR) {
64
+ return TEST_FAILURE_SCREENSHOT_UPLOAD_ERROR
65
+ }
66
+ if (uploadResult === SCREENSHOT_UPLOAD_RESULT_UPLOADED) {
67
+ return TEST_FAILURE_SCREENSHOT_UPLOADED
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Tags a test span with the aggregate screenshot upload outcome.
73
+ *
74
+ * @param {object} testSpan - Test span to tag
75
+ * @param {string|undefined} uploadResult - Aggregate screenshot upload result
76
+ * @returns {void}
77
+ */
78
+ function setScreenshotUploadTags (testSpan, uploadResult) {
79
+ const uploadTag = getScreenshotUploadTag(uploadResult)
80
+ if (uploadTag) testSpan.setTag(uploadTag, 'true')
81
+ }
82
+
83
+ module.exports = {
84
+ SCREENSHOT_UPLOAD_RESULT_ERROR,
85
+ SCREENSHOT_UPLOAD_RESULT_UPLOADED,
86
+ getScreenshotCapturedAtMs,
87
+ getScreenshotUploadResult,
88
+ getScreenshotUploadTag,
89
+ setScreenshotUploadTags,
90
+ }
@@ -16,6 +16,7 @@ export interface GeneratedConfig {
16
16
  DD_API_SECURITY_MAX_DOWNSTREAM_BODY_BYTES: number;
17
17
  DD_API_SECURITY_MAX_DOWNSTREAM_REQUEST_BODY_ANALYSIS: number;
18
18
  DD_API_SECURITY_SAMPLE_DELAY: number;
19
+ DD_APPSEC_AGENTIC_ONBOARDING: string;
19
20
  DD_APPSEC_SCA_ENABLED: boolean | undefined;
20
21
  enabled: boolean | undefined;
21
22
  eventTracking: {
@@ -431,6 +432,13 @@ export interface GeneratedConfig {
431
432
  };
432
433
  };
433
434
  };
435
+ featureFlags: {
436
+ DD_FEATURE_FLAGS_CONFIGURATION_SOURCE: "agentless" | "remote_config";
437
+ DD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_BASE_URL: string | undefined;
438
+ DD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_POLL_INTERVAL_SECONDS: number;
439
+ DD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_REQUEST_TIMEOUT_SECONDS: number;
440
+ DD_FEATURE_FLAGS_ENABLED: boolean;
441
+ };
434
442
  flushInterval: number;
435
443
  flushMinSpans: number;
436
444
  headerTags: string[];
@@ -546,6 +554,7 @@ export interface GeneratedConfig {
546
554
  DD_CIVISIBILITY_AGENTLESS_ENABLED: boolean;
547
555
  DD_CIVISIBILITY_AGENTLESS_URL: URL | undefined;
548
556
  DD_CIVISIBILITY_AUTO_INSTRUMENTATION_PROVIDER: string | undefined;
557
+ DD_CIVISIBILITY_CODE_COVERAGE_REPORT_UPLOAD_ENABLED: boolean;
549
558
  DD_CIVISIBILITY_DANGEROUSLY_FORCE_COVERAGE: boolean;
550
559
  DD_CIVISIBILITY_DANGEROUSLY_FORCE_TEST_SKIPPING: boolean;
551
560
  DD_CIVISIBILITY_EARLY_FLAKE_DETECTION_ENABLED: boolean;
@@ -561,6 +570,8 @@ export interface GeneratedConfig {
561
570
  DD_CIVISIBILITY_TEST_COMMAND: string | undefined;
562
571
  DD_CIVISIBILITY_TEST_MODULE_ID: string | undefined;
563
572
  DD_CIVISIBILITY_TEST_SESSION_ID: string | undefined;
573
+ DD_CODE_COVERAGE_FLAGS: string | undefined;
574
+ DD_TEST_EARLY_FLAKE_DETECTION_RETRY_COUNT: number | undefined;
564
575
  DD_TEST_FAILED_TEST_REPLAY_ENABLED: boolean;
565
576
  DD_TEST_FAILURE_SCREENSHOTS_ENABLED: boolean | undefined;
566
577
  DD_TEST_FLEET_CONFIG_PATH: string | undefined;
@@ -610,6 +621,7 @@ export interface GeneratedEnvVarConfig {
610
621
  DD_APM_FLUSH_DEADLINE_MILLISECONDS: number;
611
622
  DD_APM_TRACING_ENABLED: boolean;
612
623
  DD_APP_KEY: string | undefined;
624
+ DD_APPSEC_AGENTIC_ONBOARDING: string;
613
625
  DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE: string;
614
626
  DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING: string;
615
627
  DD_APPSEC_COLLECT_ALL_HEADERS: boolean;
@@ -636,6 +648,7 @@ export interface GeneratedEnvVarConfig {
636
648
  DD_CIVISIBILITY_AGENTLESS_ENABLED: boolean;
637
649
  DD_CIVISIBILITY_AGENTLESS_URL: URL | undefined;
638
650
  DD_CIVISIBILITY_AUTO_INSTRUMENTATION_PROVIDER: string | undefined;
651
+ DD_CIVISIBILITY_CODE_COVERAGE_REPORT_UPLOAD_ENABLED: boolean;
639
652
  DD_CIVISIBILITY_DANGEROUSLY_FORCE_COVERAGE: boolean;
640
653
  DD_CIVISIBILITY_DANGEROUSLY_FORCE_TEST_SKIPPING: boolean;
641
654
  DD_CIVISIBILITY_EARLY_FLAKE_DETECTION_ENABLED: boolean;
@@ -651,6 +664,7 @@ export interface GeneratedEnvVarConfig {
651
664
  DD_CIVISIBILITY_TEST_COMMAND: string | undefined;
652
665
  DD_CIVISIBILITY_TEST_MODULE_ID: string | undefined;
653
666
  DD_CIVISIBILITY_TEST_SESSION_ID: string | undefined;
667
+ DD_CODE_COVERAGE_FLAGS: string | undefined;
654
668
  DD_CODE_ORIGIN_FOR_SPANS_ENABLED: boolean;
655
669
  DD_CODE_ORIGIN_FOR_SPANS_EXPERIMENTAL_EXIT_SPANS_ENABLED: boolean;
656
670
  DD_CRASHTRACKING_ENABLED: boolean;
@@ -685,6 +699,11 @@ export interface GeneratedEnvVarConfig {
685
699
  DD_EXPERIMENTAL_TEST_OPT_VITEST_NO_WORKER_INIT: boolean | undefined;
686
700
  DD_EXPERIMENTAL_TEST_REQUESTS_FS_CACHE: boolean;
687
701
  DD_EXTERNAL_ENV: string | undefined;
702
+ DD_FEATURE_FLAGS_CONFIGURATION_SOURCE: "agentless" | "remote_config";
703
+ DD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_BASE_URL: string | undefined;
704
+ DD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_POLL_INTERVAL_SECONDS: number;
705
+ DD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_REQUEST_TIMEOUT_SECONDS: number;
706
+ DD_FEATURE_FLAGS_ENABLED: boolean;
688
707
  DD_GIT_BRANCH: string | undefined;
689
708
  DD_GIT_COMMIT_AUTHOR_DATE: string | undefined;
690
709
  DD_GIT_COMMIT_AUTHOR_EMAIL: string | undefined;
@@ -794,6 +813,7 @@ export interface GeneratedEnvVarConfig {
794
813
  DD_TELEMETRY_HEARTBEAT_INTERVAL: number;
795
814
  DD_TELEMETRY_LOG_COLLECTION_ENABLED: boolean;
796
815
  DD_TELEMETRY_METRICS_ENABLED: boolean;
816
+ DD_TEST_EARLY_FLAKE_DETECTION_RETRY_COUNT: number | undefined;
797
817
  DD_TEST_FAILED_TEST_REPLAY_ENABLED: boolean;
798
818
  DD_TEST_FAILURE_SCREENSHOTS_ENABLED: boolean | undefined;
799
819
  DD_TEST_FLEET_CONFIG_PATH: string | undefined;
@@ -13,6 +13,7 @@
13
13
  * @property {string} [namespace] Nests the canonical env name under this property path (e.g. `telemetry`).
14
14
  * @property {string} [transform]
15
15
  * @property {string} [allowed]
16
+ * @property {string} [description]
16
17
  * @property {string|boolean} [deprecated]
17
18
  * @property {boolean} [sensitive] Excludes the configuration value from configuration telemetry.
18
19
  */
@@ -333,6 +333,16 @@ class Config extends ConfigBase {
333
333
  #applyCalculated () {
334
334
  undo(this, 'calculated')
335
335
 
336
+ if (this.featureFlags.DD_FEATURE_FLAGS_ENABLED &&
337
+ !trackedConfigOrigins.has('featureFlags.DD_FEATURE_FLAGS_CONFIGURATION_SOURCE') &&
338
+ trackedConfigOrigins.has('experimental.flaggingProvider.enabled')) {
339
+ if (this.experimental.flaggingProvider.enabled) {
340
+ setAndTrack(this, 'featureFlags.DD_FEATURE_FLAGS_CONFIGURATION_SOURCE', 'remote_config')
341
+ } else {
342
+ setAndTrack(this, 'featureFlags.DD_FEATURE_FLAGS_ENABLED', false)
343
+ }
344
+ }
345
+
336
346
  if (this.url ||
337
347
  os.type() !== 'Windows_NT' &&
338
348
  !trackedConfigOrigins.has('hostname') &&
@@ -206,6 +206,15 @@
206
206
  ]
207
207
  }
208
208
  ],
209
+ "DD_APPSEC_AGENTIC_ONBOARDING": [
210
+ {
211
+ "implementation": "A",
212
+ "type": "string",
213
+ "default": "",
214
+ "namespace": "appsec",
215
+ "description": "Set automatically by Datadog's agentic onboarding solution when it configures App & API Protection for a service. Reported verbatim in configuration telemetry only; it has no effect on tracer behavior."
216
+ }
217
+ ],
209
218
  "DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE": [
210
219
  {
211
220
  "implementation": "E",
@@ -470,6 +479,14 @@
470
479
  "namespace": "testOptimization"
471
480
  }
472
481
  ],
482
+ "DD_CIVISIBILITY_CODE_COVERAGE_REPORT_UPLOAD_ENABLED": [
483
+ {
484
+ "implementation": "A",
485
+ "type": "boolean",
486
+ "default": "true",
487
+ "namespace": "testOptimization"
488
+ }
489
+ ],
473
490
  "DD_CIVISIBILITY_DANGEROUSLY_FORCE_COVERAGE": [
474
491
  {
475
492
  "implementation": "A",
@@ -494,6 +511,15 @@
494
511
  "namespace": "testOptimization"
495
512
  }
496
513
  ],
514
+ "DD_TEST_EARLY_FLAKE_DETECTION_RETRY_COUNT": [
515
+ {
516
+ "implementation": "A",
517
+ "type": "int",
518
+ "allowed": "0|[1-9][0-9]*",
519
+ "default": null,
520
+ "namespace": "testOptimization"
521
+ }
522
+ ],
497
523
  "DD_CIVISIBILITY_ENABLED": [
498
524
  {
499
525
  "implementation": "A",
@@ -597,6 +623,14 @@
597
623
  "namespace": "testOptimization"
598
624
  }
599
625
  ],
626
+ "DD_CODE_COVERAGE_FLAGS": [
627
+ {
628
+ "implementation": "A",
629
+ "type": "string",
630
+ "default": null,
631
+ "namespace": "testOptimization"
632
+ }
633
+ ],
600
634
  "DD_CODE_ORIGIN_FOR_SPANS_ENABLED": [
601
635
  {
602
636
  "implementation": "A",
@@ -810,6 +844,55 @@
810
844
  "default": "false"
811
845
  }
812
846
  ],
847
+ "DD_FEATURE_FLAGS_CONFIGURATION_SOURCE": [
848
+ {
849
+ "implementation": "A",
850
+ "type": "string",
851
+ "default": "agentless",
852
+ "allowed": "agentless|remote_config",
853
+ "description": "Experimental: Select where Feature Flagging loads Universal Flag Configuration. Supported values are agentless and remote_config.",
854
+ "namespace": "featureFlags",
855
+ "transform": "toLowerCase"
856
+ }
857
+ ],
858
+ "DD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_BASE_URL": [
859
+ {
860
+ "implementation": "A",
861
+ "type": "string",
862
+ "default": null,
863
+ "description": "Experimental: Override the agentless Feature Flagging UFC endpoint or base URL.",
864
+ "namespace": "featureFlags",
865
+ "sensitive": true
866
+ }
867
+ ],
868
+ "DD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_POLL_INTERVAL_SECONDS": [
869
+ {
870
+ "implementation": "A",
871
+ "type": "int",
872
+ "default": "30",
873
+ "description": "Experimental: Set the agentless Feature Flagging UFC polling interval in seconds, capped at one hour.",
874
+ "allowed": "[1-9]\\d*",
875
+ "namespace": "featureFlags"
876
+ }
877
+ ],
878
+ "DD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_REQUEST_TIMEOUT_SECONDS": [
879
+ {
880
+ "implementation": "A",
881
+ "type": "int",
882
+ "default": "5",
883
+ "description": "Experimental: Set the agentless Feature Flagging UFC request timeout in seconds.",
884
+ "allowed": "[1-9]\\d*",
885
+ "namespace": "featureFlags"
886
+ }
887
+ ],
888
+ "DD_FEATURE_FLAGS_ENABLED": [
889
+ {
890
+ "implementation": "A",
891
+ "type": "boolean",
892
+ "default": "true",
893
+ "namespace": "featureFlags"
894
+ }
895
+ ],
813
896
  "DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED": [
814
897
  {
815
898
  "implementation": "B",
@@ -31,6 +31,13 @@ module.exports = {
31
31
  ERROR_MESSAGE: 'error.message',
32
32
  ERROR_STACK: 'error.stack',
33
33
  IGNORE_OTEL_ERROR: Symbol('ignore.otel.error'),
34
+ // Marks an `Http2Server` that another instrumentation (currently @grpc/grpc-js)
35
+ // owns and traces through its own span lifecycle over the raw HTTP/2 stream
36
+ // API. The http2 server instrumentation reads this to leave such servers
37
+ // untraced, so a gRPC call keeps a single span with gRPC as the top frame
38
+ // instead of gaining an extra web.request span. A module-local Symbol keeps
39
+ // the mark private to dd-trace and unforgeable from user code.
40
+ FOREIGN_HTTP2_SERVER: Symbol('foreign.http2.server'),
34
41
  COMPONENT: 'component',
35
42
  CLIENT_PORT_KEY: 'network.destination.port',
36
43
  PEER_SERVICE_KEY: 'peer.service',
@@ -136,16 +136,18 @@ const DsmPathwayCodec = {
136
136
  * @param {Buffer} dataStreamsContext.hash
137
137
  * @param {number} dataStreamsContext.pathwayStartNs
138
138
  * @param {number} dataStreamsContext.edgeStartNs
139
- * @param {object} carrier
139
+ * @param {object} [carrier]
140
+ * @returns {object | undefined}
140
141
  */
141
142
  encode (dataStreamsContext, carrier) {
142
- if (!dataStreamsContext || !dataStreamsContext.hash) {
143
- return
144
- }
143
+ if (!dataStreamsContext || !dataStreamsContext.hash) return
144
+ carrier ??= {}
145
145
  carrier[CONTEXT_PROPAGATION_KEY_BASE64] = encodePathwayContextBase64(dataStreamsContext)
146
146
 
147
147
  // eslint-disable-next-line eslint-rules/eslint-log-printf-style
148
148
  log.debug(() => `Injected into DSM carrier: ${JSON.stringify(pick(carrier, logKeys))}.`)
149
+
150
+ return carrier
149
151
  },
150
152
 
151
153
  /**
@@ -19,6 +19,8 @@ module.exports = function getExporter (name) {
19
19
  return require('./ci-visibility/exporters/agentless')
20
20
  case exporters.AGENT_PROXY:
21
21
  return require('./ci-visibility/exporters/agent-proxy')
22
+ case exporters.CI_VALIDATION:
23
+ return require('./ci-visibility/exporters/ci-validation')
22
24
  case exporters.JEST_WORKER:
23
25
  case exporters.CUCUMBER_WORKER:
24
26
  case exporters.MOCHA_WORKER: