dd-trace 6.21.0 → 6.22.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 (204) hide show
  1. package/ci/diagnose.js +9 -14
  2. package/ci/init.js +1 -2
  3. package/ci/test-optimization-validation/ci-discovery.js +9 -4
  4. package/ci/test-optimization-validation/executable.js +1 -5
  5. package/ci/test-optimization-validation/framework-adapters/cypress.js +1 -4
  6. package/ci/test-optimization-validation/framework-adapters/vitest.js +1 -1
  7. package/ci/test-optimization-validation/generated-files.js +6 -4
  8. package/ci/test-optimization-validation/generated-test-contract.js +7 -3
  9. package/ci/test-optimization-validation/manifest-scaffold.js +7 -2
  10. package/ci/test-optimization-validation/manifest-schema.js +6 -7
  11. package/ci/test-optimization-validation/report-writer.js +6 -2
  12. package/ci/test-optimization-validation/runner-command.js +2 -4
  13. package/ci/test-optimization-validation/runner-contract.js +1 -2
  14. package/ci/test-optimization-validation/scenarios/auto-test-retries.js +1 -15
  15. package/ci/vitest-no-worker-init-setup.mjs +10 -7
  16. package/index.d.ts +25 -14
  17. package/initialize.mjs +1 -3
  18. package/openfeature/rules-based.d.ts +11 -0
  19. package/openfeature/rules-based.js +7 -0
  20. package/package.json +12 -10
  21. package/packages/datadog-core/src/storage.js +16 -0
  22. package/packages/datadog-esbuild/index.js +18 -12
  23. package/packages/datadog-instrumentations/src/aerospike.js +3 -3
  24. package/packages/datadog-instrumentations/src/ai.js +3 -3
  25. package/packages/datadog-instrumentations/src/aws-durable-execution-sdk-js.js +0 -6
  26. package/packages/datadog-instrumentations/src/aws-sdk.js +21 -13
  27. package/packages/datadog-instrumentations/src/cassandra-driver.js +25 -20
  28. package/packages/datadog-instrumentations/src/claude-agent-sdk.js +9 -23
  29. package/packages/datadog-instrumentations/src/cucumber.js +48 -33
  30. package/packages/datadog-instrumentations/src/elasticsearch.js +1 -5
  31. package/packages/datadog-instrumentations/src/electron.js +6 -6
  32. package/packages/datadog-instrumentations/src/fastify.js +13 -4
  33. package/packages/datadog-instrumentations/src/fs.js +2 -2
  34. package/packages/datadog-instrumentations/src/google-cloud-pubsub.js +9 -5
  35. package/packages/datadog-instrumentations/src/grpc/client.js +2 -0
  36. package/packages/datadog-instrumentations/src/helpers/bundler-register.js +2 -2
  37. package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +1 -2
  38. package/packages/datadog-instrumentations/src/helpers/instrument.js +1 -1
  39. package/packages/datadog-instrumentations/src/helpers/register.js +36 -1
  40. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +2 -0
  41. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentation-registry.js +87 -6
  42. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql-jit.js +5 -6
  43. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/postgres.js +48 -31
  44. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +7 -7
  45. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms/postgres.js +42 -4
  46. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +7 -10
  47. package/packages/datadog-instrumentations/src/ioredis.js +7 -5
  48. package/packages/datadog-instrumentations/src/iovalkey.js +7 -5
  49. package/packages/datadog-instrumentations/src/jest.js +3 -10
  50. package/packages/datadog-instrumentations/src/knex.js +9 -5
  51. package/packages/datadog-instrumentations/src/mariadb-bundle.js +3 -3
  52. package/packages/datadog-instrumentations/src/mocha/common.js +20 -8
  53. package/packages/datadog-instrumentations/src/mocha/main.js +140 -43
  54. package/packages/datadog-instrumentations/src/mocha/utils.js +3 -2
  55. package/packages/datadog-instrumentations/src/mocha/worker.js +1 -2
  56. package/packages/datadog-instrumentations/src/mongodb-core.js +3 -3
  57. package/packages/datadog-instrumentations/src/mongoose.js +3 -3
  58. package/packages/datadog-instrumentations/src/nova-sonic/audio.js +173 -0
  59. package/packages/datadog-instrumentations/src/nova-sonic/index.js +209 -0
  60. package/packages/datadog-instrumentations/src/nova-sonic/session.js +483 -0
  61. package/packages/datadog-instrumentations/src/openai-realtime/index.js +18 -6
  62. package/packages/datadog-instrumentations/src/openai-realtime/session.js +1 -1
  63. package/packages/datadog-instrumentations/src/openai.js +7 -0
  64. package/packages/datadog-instrumentations/src/pino.js +5 -6
  65. package/packages/datadog-instrumentations/src/redis.js +27 -13
  66. package/packages/datadog-instrumentations/src/router.js +30 -0
  67. package/packages/datadog-instrumentations/src/rum-browser-scripts.js +2 -6
  68. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +7 -5
  69. package/packages/datadog-instrumentations/src/vitest-main.js +9 -15
  70. package/packages/datadog-instrumentations/src/vitest-util.js +2 -2
  71. package/packages/datadog-instrumentations/src/vitest-worker.js +1 -2
  72. package/packages/datadog-instrumentations/src/vitest.js +2 -2
  73. package/packages/datadog-instrumentations/src/webdriverio.js +5 -8
  74. package/packages/datadog-instrumentations/src/ws.js +1 -4
  75. package/packages/datadog-plugin-amqplib/src/client.js +1 -2
  76. package/packages/datadog-plugin-avsc/src/schema_iterator.js +1 -5
  77. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/handler.js +1 -2
  78. package/packages/datadog-plugin-aws-sdk/src/base.js +5 -8
  79. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/index.js +18 -0
  80. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/sonic.js +45 -0
  81. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +9 -3
  82. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +2 -5
  83. package/packages/datadog-plugin-azure-durable-functions/src/index.js +5 -4
  84. package/packages/datadog-plugin-azure-functions/src/index.js +5 -3
  85. package/packages/datadog-plugin-claude-agent-sdk/src/index.js +0 -2
  86. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +1 -5
  87. package/packages/datadog-plugin-cypress/src/index.js +6 -6
  88. package/packages/datadog-plugin-cypress/src/source-map-utils.js +1 -2
  89. package/packages/datadog-plugin-cypress/src/support.js +1 -4
  90. package/packages/datadog-plugin-express/src/code_origin.js +1 -2
  91. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -2
  92. package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +1 -2
  93. package/packages/datadog-plugin-graphql/src/execute.js +5 -5
  94. package/packages/datadog-plugin-grpc/src/util.js +1 -2
  95. package/packages/datadog-plugin-kafkajs/src/producer.js +1 -2
  96. package/packages/datadog-plugin-mocha/src/index.js +7 -10
  97. package/packages/datadog-plugin-mocha/src/webdriverio-video.js +8 -4
  98. package/packages/datadog-plugin-mongodb-core/src/query.js +8 -5
  99. package/packages/datadog-plugin-next/src/index.js +1 -2
  100. package/packages/datadog-plugin-openai/src/tracing.js +5 -2
  101. package/packages/datadog-plugin-openai-agents/src/integration.js +6 -2
  102. package/packages/datadog-plugin-openai-agents/src/processor.js +2 -4
  103. package/packages/datadog-plugin-postgres/src/index.js +24 -0
  104. package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +1 -5
  105. package/packages/datadog-plugin-rhea/src/consumer.js +1 -4
  106. package/packages/datadog-plugin-vitest/src/index.js +5 -2
  107. package/packages/datadog-shimmer/src/shimmer.js +5 -4
  108. package/packages/datadog-webpack/index.js +55 -43
  109. package/packages/datadog-webpack/src/loader.js +56 -8
  110. package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +2 -4
  111. package/packages/dd-trace/src/aiguard/messages/utils.js +1 -2
  112. package/packages/dd-trace/src/aiguard/redaction.js +8 -2
  113. package/packages/dd-trace/src/appsec/api_security/normalized-route.js +3 -2
  114. package/packages/dd-trace/src/appsec/api_security/sampler.js +2 -6
  115. package/packages/dd-trace/src/appsec/downstream_requests.js +1 -9
  116. package/packages/dd-trace/src/appsec/handlers/http-request.js +3 -9
  117. package/packages/dd-trace/src/appsec/handlers/http-response.js +7 -2
  118. package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +4 -3
  119. package/packages/dd-trace/src/appsec/iast/analyzers/set-cookies-header-interceptor.js +1 -4
  120. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations-taint-object.js +7 -1
  121. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +1 -3
  122. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-esm.mjs +1 -3
  123. package/packages/dd-trace/src/appsec/iast/telemetry/iast-metric.js +1 -6
  124. package/packages/dd-trace/src/appsec/lambda.js +1 -3
  125. package/packages/dd-trace/src/appsec/rasp/fs-plugin.js +37 -27
  126. package/packages/dd-trace/src/appsec/rasp/lfi.js +9 -5
  127. package/packages/dd-trace/src/appsec/reporter.js +21 -1
  128. package/packages/dd-trace/src/appsec/stack_trace.js +1 -2
  129. package/packages/dd-trace/src/ci-visibility/dynamic-atr-retries.js +3 -6
  130. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/msgpack-to-json.js +1 -1
  131. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +13 -7
  132. package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +1 -2
  133. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +2 -8
  134. package/packages/dd-trace/src/ci-visibility/resolve-runfile-path.js +54 -0
  135. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +5 -8
  136. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +2 -53
  137. package/packages/dd-trace/src/config/config-types.d.ts +2 -2
  138. package/packages/dd-trace/src/config/defaults.js +5 -5
  139. package/packages/dd-trace/src/config/generated-config-types.d.ts +8 -0
  140. package/packages/dd-trace/src/config/helper.js +34 -3
  141. package/packages/dd-trace/src/config/index.js +10 -9
  142. package/packages/dd-trace/src/config/normalize-service.js +1 -1
  143. package/packages/dd-trace/src/config/parsers.js +2 -2
  144. package/packages/dd-trace/src/config/supported-configurations.json +35 -0
  145. package/packages/dd-trace/src/datastreams/processor.js +3 -2
  146. package/packages/dd-trace/src/datastreams/schemas/schema_builder.js +1 -4
  147. package/packages/dd-trace/src/debugger/devtools_client/send.js +1 -0
  148. package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -0
  149. package/packages/dd-trace/src/debugger/guardrail-metrics.js +2 -2
  150. package/packages/dd-trace/src/dogstatsd.js +1 -1
  151. package/packages/dd-trace/src/encode/tags-processors.js +2 -2
  152. package/packages/dd-trace/src/exporters/common/request.js +1 -5
  153. package/packages/dd-trace/src/id.js +14 -1
  154. package/packages/dd-trace/src/lambda/handler.js +179 -19
  155. package/packages/dd-trace/src/lambda/index.js +15 -1
  156. package/packages/dd-trace/src/lambda/runtime/patch.js +34 -7
  157. package/packages/dd-trace/src/llmobs/audio-utils.js +3 -2
  158. package/packages/dd-trace/src/llmobs/constants/tags.js +12 -0
  159. package/packages/dd-trace/src/llmobs/experiments/experiment.js +1 -1
  160. package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +15 -0
  161. package/packages/dd-trace/src/llmobs/plugins/genai/util.js +1 -2
  162. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chat_model.js +1 -3
  163. package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
  164. package/packages/dd-trace/src/llmobs/plugins/nova-sonic.js +92 -0
  165. package/packages/dd-trace/src/llmobs/plugins/openai/index.js +56 -2
  166. package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +38 -0
  167. package/packages/dd-trace/src/llmobs/prompts/manager.js +5 -7
  168. package/packages/dd-trace/src/llmobs/prompts/prompt.js +1 -1
  169. package/packages/dd-trace/src/llmobs/span_processor.js +1 -2
  170. package/packages/dd-trace/src/llmobs/tagger.js +2 -2
  171. package/packages/dd-trace/src/llmobs/util.js +1 -2
  172. package/packages/dd-trace/src/llmobs/writers/base.js +4 -5
  173. package/packages/dd-trace/src/log/log.js +1 -5
  174. package/packages/dd-trace/src/openfeature/evaluation-telemetry-opt-out-hook.js +51 -0
  175. package/packages/dd-trace/src/openfeature/flagging_provider.js +48 -21
  176. package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +1 -1
  177. package/packages/dd-trace/src/opentelemetry/metrics/otlp_transformer.js +1 -1
  178. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +1 -2
  179. package/packages/dd-trace/src/opentelemetry/otlp/common.proto +1 -1
  180. package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +6 -23
  181. package/packages/dd-trace/src/opentracing/propagation/text_map.js +53 -13
  182. package/packages/dd-trace/src/otel-sampling.js +4 -5
  183. package/packages/dd-trace/src/otel-thread-ctx.js +79 -13
  184. package/packages/dd-trace/src/plugins/database.js +7 -16
  185. package/packages/dd-trace/src/plugins/util/git.js +9 -25
  186. package/packages/dd-trace/src/plugins/util/inferred_proxy.js +1 -5
  187. package/packages/dd-trace/src/plugins/util/test.js +48 -4
  188. package/packages/dd-trace/src/priority_sampler.js +1 -2
  189. package/packages/dd-trace/src/profiling/profiler.js +4 -6
  190. package/packages/dd-trace/src/proxy.js +1 -2
  191. package/packages/dd-trace/src/ritm.js +4 -2
  192. package/packages/dd-trace/src/serverless.js +5 -0
  193. package/packages/dd-trace/src/span_processor.js +13 -17
  194. package/packages/dd-trace/src/telemetry/endpoints.js +1 -3
  195. package/packages/dd-trace/src/util.js +29 -0
  196. package/packages/dd-trace/src/web-tags-cache.js +6 -2
  197. package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
  198. package/vendor/dist/@datadog/openfeature-node-server/rules-based/LICENSE +201 -0
  199. package/vendor/dist/@datadog/openfeature-node-server/rules-based/index.d.ts +925 -0
  200. package/vendor/dist/@datadog/openfeature-node-server/rules-based/index.js +1 -0
  201. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  202. package/vendor/dist/protobufjs/index.js +1 -1
  203. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  204. package/vendor/dist/shell-quote/index.js +1 -1
@@ -8,7 +8,7 @@ const web = require('../plugins/util/web')
8
8
  const { ipHeaderList } = require('../plugins/util/ip_extractor')
9
9
  const { keepTrace } = require('../priority_sampler')
10
10
  const { ASM } = require('../standalone/product')
11
- const { isEmpty, truncateString } = require('../util')
11
+ const { isEmpty, truncateString, typedArrayLength } = require('../util')
12
12
  const { getActiveRequest } = require('./store')
13
13
  const {
14
14
  incrementWafInitMetric,
@@ -431,6 +431,20 @@ function reportAttack ({ events: attackData, actions }, req, rootSpan) {
431
431
  }
432
432
  }
433
433
 
434
+ /**
435
+ * Copies the first elements by index, the one typed array access that user code cannot override, unlike `subarray`,
436
+ * `toJSON` or their species constructors. The result keeps the shape `Buffer#toJSON` and `Object.keys` produce.
437
+ *
438
+ * @param {import('../util').BinaryView} view
439
+ */
440
+ function truncateTypedArray (view) {
441
+ const data = new Array(COLLECTED_REQUEST_BODY_MAX_ELEMENTS_PER_NODE)
442
+ for (let i = 0; i < COLLECTED_REQUEST_BODY_MAX_ELEMENTS_PER_NODE; i++) {
443
+ data[i] = view[i]
444
+ }
445
+ return Buffer.isBuffer(view) ? { type: 'Buffer', data } : { ...data }
446
+ }
447
+
434
448
  function truncateRequestBody (target, depth = 0) {
435
449
  switch (typeof target) {
436
450
  case 'string':
@@ -447,6 +461,12 @@ function truncateRequestBody (target, depth = 0) {
447
461
  return { truncated: true }
448
462
  }
449
463
 
464
+ // `Buffer#toJSON` and `Object.keys` both materialize one entry per byte, so cut large binary views down
465
+ // before they are serialized.
466
+ if (ArrayBuffer.isView(target) && typedArrayLength(target) > COLLECTED_REQUEST_BODY_MAX_ELEMENTS_PER_NODE) {
467
+ return { value: truncateTypedArray(target), truncated: true }
468
+ }
469
+
450
470
  if (typeof target.toJSON === 'function') {
451
471
  try {
452
472
  return truncateRequestBody(target.toJSON(), depth + 1)
@@ -81,8 +81,7 @@ function getCallsiteFrames (maxDepth = 32, constructorOpt = getCallsiteFrames, c
81
81
  }
82
82
 
83
83
  function reportStackTrace (rootSpan, stackId, frames, namespace = STACK_TRACE_NAMESPACES.RASP) {
84
- if (!rootSpan) return
85
- if (!Array.isArray(frames)) return
84
+ if (!rootSpan || !Array.isArray(frames)) return
86
85
 
87
86
  if (!rootSpan.meta_struct) {
88
87
  rootSpan.meta_struct = {}
@@ -86,12 +86,9 @@ function getDynamicAtrBuckets (value) {
86
86
  * @param {number[] | null} customBuckets
87
87
  */
88
88
  function getDynamicAtrRetryCount (durationMs, efdRetryPolicy, customBuckets) {
89
- let retryCount
90
- if (customBuckets) {
91
- retryCount = customBuckets[retryBucketIndexForDuration(durationMs)]
92
- } else {
93
- retryCount = retriesForDuration(durationMs, efdRetryPolicy)
94
- }
89
+ const retryCount = customBuckets
90
+ ? customBuckets[retryBucketIndexForDuration(durationMs)]
91
+ : retriesForDuration(durationMs, efdRetryPolicy)
95
92
  return Math.max(1, retryCount)
96
93
  }
97
94
 
@@ -224,7 +224,7 @@ class MsgpackJsonConverter {
224
224
  * @param {number} value decoded floating-point value
225
225
  */
226
226
  #writeFloat (value) {
227
- if (!Number.isFinite(value)) throw new Error('MessagePack validation payload contains a non-finite number.')
227
+ if (!Number.isFinite(value)) throw new TypeError('MessagePack validation payload contains a non-finite number.')
228
228
  this.#writer.write(Object.is(value, -0) ? '-0' : String(value))
229
229
  }
230
230
 
@@ -114,10 +114,14 @@ function isValidRetryCount (value) {
114
114
  * @param {unknown} retryPolicy - Candidate retry policy.
115
115
  */
116
116
  function isValidCachedEfdRetryPolicy (retryPolicy) {
117
- if (retryPolicy === null || typeof retryPolicy !== 'object' || Array.isArray(retryPolicy)) return false
118
- if (!isValidRetryCount(retryPolicy.schedulingRetryCount)) return false
119
- if (!Array.isArray(retryPolicy.durationRetryCounts) ||
120
- retryPolicy.durationRetryCounts.length !== EMPTY_EFD_RETRY_POLICY.durationRetryCounts.length) {
117
+ if (
118
+ retryPolicy === null ||
119
+ typeof retryPolicy !== 'object' ||
120
+ Array.isArray(retryPolicy) ||
121
+ !isValidRetryCount(retryPolicy.schedulingRetryCount) ||
122
+ !Array.isArray(retryPolicy.durationRetryCounts) ||
123
+ retryPolicy.durationRetryCounts.length !== EMPTY_EFD_RETRY_POLICY.durationRetryCounts.length
124
+ ) {
121
125
  return false
122
126
  }
123
127
 
@@ -152,9 +156,11 @@ function isValidCachedSettings (settings) {
152
156
  }
153
157
  if (settings.isDiEnabled && !settings.isFlakyTestRetriesEnabled) return false
154
158
  if (settings.isEarlyFlakeDetectionEnabled && !settings.isKnownTestsEnabled) return false
155
- if (!isValidCachedEfdRetryPolicy(settings.earlyFlakeDetectionRetryPolicy)) return false
156
- if (!isNonNegativeSafeInteger(settings.earlyFlakeDetectionFaultyThreshold) ||
157
- settings.earlyFlakeDetectionFaultyThreshold > 100) {
159
+ if (
160
+ !isValidCachedEfdRetryPolicy(settings.earlyFlakeDetectionRetryPolicy) ||
161
+ !isNonNegativeSafeInteger(settings.earlyFlakeDetectionFaultyThreshold) ||
162
+ settings.earlyFlakeDetectionFaultyThreshold > 100
163
+ ) {
158
164
  return false
159
165
  }
160
166
  return settings.testManagementAttemptToFixRetries === undefined ||
@@ -12,8 +12,7 @@ const getConfig = require('../../config')
12
12
  * @param {object} configuration - Request configuration for the settings endpoint.
13
13
  */
14
14
  function buildSettingsCacheKey (configuration) {
15
- const config = getConfig()
16
- const { testOptimization } = config
15
+ const { testOptimization } = getConfig()
17
16
  return buildCacheKey('settings', [
18
17
  tracerVersion,
19
18
  configuration.url?.href,
@@ -35,10 +35,7 @@ function getInterprocessTraceCode () {
35
35
  if (DD_PLAYWRIGHT_WORKER) {
36
36
  return PLAYWRIGHT_WORKER_TRACE_PAYLOAD_CODE
37
37
  }
38
- if (getEnvironmentVariable('TINYPOOL_WORKER_ID')) {
39
- return VITEST_WORKER_TRACE_PAYLOAD_CODE
40
- }
41
- if (DD_VITEST_WORKER) {
38
+ if (getEnvironmentVariable('TINYPOOL_WORKER_ID') || DD_VITEST_WORKER) {
42
39
  return VITEST_WORKER_TRACE_PAYLOAD_CODE
43
40
  }
44
41
  return null
@@ -62,10 +59,7 @@ function getInterprocessLogsCode () {
62
59
  if (getEnvironmentVariable('MOCHA_WORKER_ID') === 'webdriverio') {
63
60
  return MOCHA_WORKER_LOGS_PAYLOAD_CODE
64
61
  }
65
- if (getEnvironmentVariable('TINYPOOL_WORKER_ID')) {
66
- return VITEST_WORKER_LOGS_PAYLOAD_CODE
67
- }
68
- if (getConfig().DD_VITEST_WORKER) {
62
+ if (getEnvironmentVariable('TINYPOOL_WORKER_ID') || getConfig().DD_VITEST_WORKER) {
69
63
  return VITEST_WORKER_LOGS_PAYLOAD_CODE
70
64
  }
71
65
  return null
@@ -0,0 +1,54 @@
1
+ 'use strict'
2
+
3
+ const fs = require('node:fs')
4
+ const path = require('node:path')
5
+
6
+ const log = require('../log')
7
+
8
+ /**
9
+ * @param {string} filePath
10
+ * @param {Record<string, string | undefined>} env
11
+ */
12
+ function resolveRunfilePath (filePath, env) {
13
+ if (fs.existsSync(filePath)) return filePath
14
+
15
+ const runfilesDir = env.RUNFILES_DIR
16
+ if (runfilesDir) {
17
+ const candidate = path.join(runfilesDir, filePath)
18
+ if (fs.existsSync(candidate)) return candidate
19
+ }
20
+
21
+ const manifestCandidate = resolveFromManifest(filePath, env.RUNFILES_MANIFEST_FILE)
22
+ if (manifestCandidate) return manifestCandidate
23
+
24
+ const testSrcdir = env.TEST_SRCDIR
25
+ if (testSrcdir) {
26
+ const candidate = path.join(testSrcdir, filePath)
27
+ if (fs.existsSync(candidate)) return candidate
28
+ }
29
+
30
+ return filePath
31
+ }
32
+
33
+ /**
34
+ * @param {string} filePath
35
+ * @param {string | undefined} runfilesManifest
36
+ */
37
+ function resolveFromManifest (filePath, runfilesManifest) {
38
+ if (!runfilesManifest || !fs.existsSync(runfilesManifest)) return
39
+
40
+ try {
41
+ const lines = fs.readFileSync(runfilesManifest, 'utf8').split('\n')
42
+ for (const line of lines) {
43
+ const separatorIndex = line.indexOf(' ')
44
+ if (separatorIndex <= 0 || line.slice(0, separatorIndex) !== filePath) continue
45
+
46
+ const resolvedPath = line.slice(separatorIndex + 1).trim()
47
+ if (resolvedPath) return resolvedPath
48
+ }
49
+ } catch (err) {
50
+ log.debug('Failed to resolve Test Optimization runfile from %s: %s', runfilesManifest, err.message)
51
+ }
52
+ }
53
+
54
+ module.exports = resolveRunfilePath
@@ -75,14 +75,12 @@ function validateSettingsResponse (response, options = {}) {
75
75
  assertBooleanFields(attributes.test_management, ['enabled'], 'settings test_management')
76
76
  assertOptionalBoundedNumber(
77
77
  attributes.early_flake_detection.faulty_session_threshold,
78
- 'settings early_flake_detection faulty_session_threshold',
79
- { integer: true }
78
+ 'settings early_flake_detection faulty_session_threshold'
80
79
  )
81
80
  assertRetryMap(attributes.early_flake_detection.slow_test_retries)
82
81
  assertOptionalBoundedNumber(
83
82
  attributes.test_management.attempt_to_fix_retries,
84
- 'settings test_management attempt_to_fix_retries',
85
- { integer: true }
83
+ 'settings test_management attempt_to_fix_retries'
86
84
  )
87
85
  }
88
86
 
@@ -214,10 +212,9 @@ function assertBooleanFields (object, keys, endpoint) {
214
212
  }
215
213
  }
216
214
 
217
- function assertOptionalBoundedNumber (value, endpoint, { integer = false } = {}) {
215
+ function assertOptionalBoundedNumber (value, endpoint) {
218
216
  if (value === undefined) return
219
- if (!Number.isFinite(value) || (integer && !Number.isInteger(value)) ||
220
- value < 0 || value > MAX_RETRIES) {
217
+ if (!Number.isFinite(value) || !Number.isInteger(value) || value < 0 || value > MAX_RETRIES) {
221
218
  throw new TypeError(`Invalid ${endpoint}: value must be between 0 and ${MAX_RETRIES}`)
222
219
  }
223
220
  }
@@ -230,7 +227,7 @@ function assertRetryMap (retries) {
230
227
  if (!RETRY_THRESHOLD_PATTERN.test(threshold)) {
231
228
  throw new TypeError('Invalid early flake detection retry threshold: expected a duration such as 5s')
232
229
  }
233
- assertOptionalBoundedNumber(count, `early flake detection retry count ${threshold}`, { integer: true })
230
+ assertOptionalBoundedNumber(count, `early flake detection retry count ${threshold}`)
234
231
  }
235
232
  }
236
233
 
@@ -5,6 +5,7 @@ const path = require('node:path')
5
5
 
6
6
  const log = require('../log')
7
7
  const { getNumFromKnownTests } = require('../plugins/util/test')
8
+ const resolveRunfilePath = require('./resolve-runfile-path')
8
9
  const { parseKnownTestsResponse } = require('./early-flake-detection/get-known-tests')
9
10
  const {
10
11
  logSkippableSuitesResponse,
@@ -38,16 +39,12 @@ const HTTP_CACHE_FOLDER_NAME = 'http'
38
39
 
39
40
  const ENV_MANIFEST_FILE = 'TEST_OPTIMIZATION_MANIFEST_FILE'
40
41
  const ENV_MANIFEST_FILE_ALIAS = 'DD_TEST_OPTIMIZATION_MANIFEST_FILE'
41
- const ENV_RUNFILES_DIR = 'RUNFILES_DIR'
42
- const ENV_RUNFILES_MANIFEST_FILE = 'RUNFILES_MANIFEST_FILE'
43
- const ENV_TEST_SRCDIR = 'TEST_SRCDIR'
44
42
 
45
43
  const SETTINGS_FILE_NAME = 'settings.json'
46
44
  const KNOWN_TESTS_FILE_NAME = 'known_tests.json'
47
45
  const SKIPPABLE_TESTS_FILE_NAME = 'skippable_tests.json'
48
46
  const TEST_MANAGEMENT_FILE_NAME = 'test_management.json'
49
47
 
50
- const RUNFILES_MANIFEST_SEPARATOR = ' '
51
48
  const DEFAULT_VALIDATION_MAX_FILE_BYTES = 1024 * 1024
52
49
  const DEFAULT_VALIDATION_MAX_ENTRIES = 100_000
53
50
  const DEFAULT_VALIDATION_MAX_NESTING_DEPTH = 32
@@ -250,7 +247,7 @@ class TestOptimizationHttpCache {
250
247
  const manifestFile = this._env[ENV_MANIFEST_FILE] || this._env[ENV_MANIFEST_FILE_ALIAS]
251
248
  if (!manifestFile) return
252
249
 
253
- const resolvedManifestPath = this._resolveRunfilePath(manifestFile)
250
+ const resolvedManifestPath = resolveRunfilePath(manifestFile, this._env)
254
251
  if (fs.existsSync(resolvedManifestPath)) {
255
252
  return resolvedManifestPath
256
253
  }
@@ -280,54 +277,6 @@ class TestOptimizationHttpCache {
280
277
  }
281
278
  }
282
279
 
283
- _resolveRunfilePath (manifestFile) {
284
- if (fs.existsSync(manifestFile)) {
285
- return manifestFile
286
- }
287
-
288
- const runfilesDir = this._env[ENV_RUNFILES_DIR]
289
- if (runfilesDir) {
290
- const candidate = path.join(runfilesDir, manifestFile)
291
- if (fs.existsSync(candidate)) {
292
- return candidate
293
- }
294
- }
295
-
296
- const runfilesManifestCandidate = this._resolveRunfilePathFromManifest(manifestFile)
297
- if (runfilesManifestCandidate) {
298
- return runfilesManifestCandidate
299
- }
300
-
301
- const testSrcdir = this._env[ENV_TEST_SRCDIR]
302
- if (testSrcdir) {
303
- const candidate = path.join(testSrcdir, manifestFile)
304
- if (fs.existsSync(candidate)) {
305
- return candidate
306
- }
307
- }
308
-
309
- return manifestFile
310
- }
311
-
312
- _resolveRunfilePathFromManifest (manifestFile) {
313
- const runfilesManifest = this._env[ENV_RUNFILES_MANIFEST_FILE]
314
- if (!runfilesManifest || !fs.existsSync(runfilesManifest)) return
315
-
316
- try {
317
- const lines = fs.readFileSync(runfilesManifest, 'utf8').split('\n')
318
- for (const line of lines) {
319
- const separatorIndex = line.indexOf(RUNFILES_MANIFEST_SEPARATOR)
320
- if (separatorIndex <= 0) continue
321
- if (line.slice(0, separatorIndex) !== manifestFile) continue
322
-
323
- const resolvedPath = line.slice(separatorIndex + 1).trim()
324
- if (resolvedPath) return resolvedPath
325
- }
326
- } catch (err) {
327
- log.debug('Failed to resolve Test Optimization HTTP cache manifest from %s: %s', runfilesManifest, err.message)
328
- }
329
- }
330
-
331
280
  _readManifestVersion (manifestPath) {
332
281
  try {
333
282
  this._assertValidationFixtureFile(manifestPath)
@@ -72,9 +72,9 @@ export type ConfigPathValue<TPath extends ConfigPath> = ConfigPathValueFor<Confi
72
72
  export type ConfigDefaults = Partial<{ [TPath in ConfigPath]: ConfigPathValue<TPath> }>
73
73
  export type ConfigurationOption = {
74
74
  property?: string
75
- parser?: (value: unknown, optionName: string, source: string) => unknown
75
+ parser?: (value: unknown, optionName: string, source: string, reportInvalid?: boolean) => unknown
76
76
  type: string
77
77
  canonicalName?: string
78
- transformer?: (value: unknown, optionName: string, source: string) => unknown
78
+ transformer?: (value: unknown, optionName: string, source: string, reportInvalid?: boolean) => unknown
79
79
  telemetryTransformer?: (value: unknown) => unknown
80
80
  }
@@ -191,10 +191,10 @@ const optionsTable = {
191
191
  },
192
192
  }
193
193
 
194
- const parser = (value, optionName, source) => {
194
+ const parser = (value, optionName, source, reportInvalid = true) => {
195
195
  const { type, canonicalName = optionName } = configurationsTable[optionName]
196
196
  const parsed = parsers[type](value, canonicalName)
197
- if (parsed === undefined) {
197
+ if (parsed === undefined && reportInvalid) {
198
198
  warnInvalidValue(value, optionName, source, `Invalid ${type} input`)
199
199
  }
200
200
  return parsed
@@ -249,13 +249,13 @@ for (const [canonicalName, entries] of Object.entries(supportedConfigurations))
249
249
  regExps[entry.allowed] ??= new RegExp(`^(${entry.allowed})$`, 'i')
250
250
  const allowed = regExps[entry.allowed]
251
251
  const originalTransform = transformer
252
- transformer = (value, optionName, source) => {
252
+ transformer = (value, optionName, source, reportInvalid = true) => {
253
253
  if (!allowed.test(value)) {
254
- warnInvalidValue(value, optionName, source, 'Invalid value')
254
+ if (reportInvalid) warnInvalidValue(value, optionName, source, 'Invalid value')
255
255
  return
256
256
  }
257
257
  if (originalTransform) {
258
- value = originalTransform(value)
258
+ value = originalTransform(value, optionName, source, reportInvalid)
259
259
  }
260
260
  return value
261
261
  }
@@ -3,6 +3,8 @@
3
3
 
4
4
  export interface GeneratedConfig {
5
5
  _DD_APM_TRACING_AGENTLESS_ENABLED: boolean;
6
+ _DD_TEST_PARALLELIZATION_ENABLED: boolean | undefined;
7
+ _DD_TEST_PARALLELIZATION_METADATA_FILE: string | undefined;
6
8
  _DD_TRACE_METRICS_OTEL_FLUSH_INTERVAL: number;
7
9
  aiguard: {
8
10
  DD_AI_GUARD_ANALYZE_STREAM_RESPONSES_ENABLED: boolean;
@@ -75,6 +77,7 @@ export interface GeneratedConfig {
75
77
  DD_APM_FLUSH_DEADLINE_MILLISECONDS: number;
76
78
  DD_APP_KEY: string | undefined;
77
79
  DD_AZURE_RESOURCE_GROUP: string | undefined;
80
+ DD_BUNDLER_INJECT_GIT_METADATA: boolean;
78
81
  DD_CRASHTRACKING_ENABLED: boolean;
79
82
  DD_CUSTOM_PARENT_ID: string | undefined;
80
83
  DD_CUSTOM_TRACE_ID: string | undefined;
@@ -435,6 +438,7 @@ export interface GeneratedConfig {
435
438
  DD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_POLL_INTERVAL_SECONDS: number;
436
439
  DD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_REQUEST_TIMEOUT_SECONDS: number;
437
440
  DD_FEATURE_FLAGS_ENABLED: boolean;
441
+ DD_FEATURE_FLAGS_EVALUATION_TELEMETRY_PER_SUBJECT_OPT_OUT_ENABLED: boolean;
438
442
  DD_FLAGGING_EVALUATION_COUNTS_ENABLED: boolean;
439
443
  };
440
444
  flushInterval: number;
@@ -606,6 +610,8 @@ export interface GeneratedConfig {
606
610
 
607
611
  export interface GeneratedEnvVarConfig {
608
612
  _DD_APM_TRACING_AGENTLESS_ENABLED: boolean;
613
+ _DD_TEST_PARALLELIZATION_ENABLED: boolean | undefined;
614
+ _DD_TEST_PARALLELIZATION_METADATA_FILE: string | undefined;
609
615
  _DD_TRACE_METRICS_OTEL_FLUSH_INTERVAL: number;
610
616
  DATADOG_API_KEY: string | undefined;
611
617
  DD_ACTION_EXECUTION_ID: string | undefined;
@@ -656,6 +662,7 @@ export interface GeneratedEnvVarConfig {
656
662
  DD_APPSEC_TRACE_RATE_LIMIT: number;
657
663
  DD_APPSEC_WAF_TIMEOUT: number;
658
664
  DD_AZURE_RESOURCE_GROUP: string | undefined;
665
+ DD_BUNDLER_INJECT_GIT_METADATA: boolean;
659
666
  DD_CIVISIBILITY_AGENTLESS_ENABLED: boolean;
660
667
  DD_CIVISIBILITY_AGENTLESS_URL: URL | undefined;
661
668
  DD_CIVISIBILITY_AUTO_INSTRUMENTATION_PROVIDER: string | undefined;
@@ -718,6 +725,7 @@ export interface GeneratedEnvVarConfig {
718
725
  DD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_POLL_INTERVAL_SECONDS: number;
719
726
  DD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_REQUEST_TIMEOUT_SECONDS: number;
720
727
  DD_FEATURE_FLAGS_ENABLED: boolean;
728
+ DD_FEATURE_FLAGS_EVALUATION_TELEMETRY_PER_SUBJECT_OPT_OUT_ENABLED: boolean;
721
729
  DD_FLAGGING_EVALUATION_COUNTS_ENABLED: boolean;
722
730
  DD_GIT_BRANCH: string | undefined;
723
731
  DD_GIT_COMMIT_AUTHOR_DATE: string | undefined;
@@ -180,14 +180,17 @@ function loadConfigurationsTable () {
180
180
  * @param {string} name Canonical or alias environment variable name.
181
181
  * @param {string} value Raw value read from an environment source.
182
182
  * @param {'env_var'|'fleet_stable_config'|'local_stable_config'} source Source the value was read from.
183
+ * @param {boolean} [reportInvalid] Whether invalid values should produce configuration diagnostics.
183
184
  * @returns {string | number | boolean | object | undefined}
184
185
  */
185
- function parseConfigurationValue (name, value, source) {
186
+ function parseConfigurationValue (name, value, source, reportInvalid = true) {
186
187
  loadConfigurationsTable()
187
188
  const canonical = aliasToCanonical[name] ?? name
188
189
  const entry = configurationsTable[canonical]
189
- const parsed = entry.parser(value, canonical, source)
190
- return parsed !== undefined && entry.transformer ? entry.transformer(parsed, canonical, source) : parsed
190
+ const parsed = entry.parser(value, canonical, source, reportInvalid)
191
+ return parsed !== undefined && entry.transformer
192
+ ? entry.transformer(parsed, canonical, source, reportInvalid)
193
+ : parsed
191
194
  }
192
195
 
193
196
  /**
@@ -376,4 +379,32 @@ module.exports = {
376
379
  }
377
380
  }
378
381
  },
382
+
383
+ /**
384
+ * Returns the environment variable name from the highest-priority source whose value is valid.
385
+ *
386
+ * @param {string} name Environment variable name
387
+ */
388
+ getValidatedConfiguredEnvName (name) {
389
+ validateAccess(name)
390
+
391
+ if (!stableConfigLoaded) {
392
+ loadStableConfig()
393
+ }
394
+
395
+ const sources = [
396
+ [fleetStableConfig, 'fleet_stable_config'],
397
+ [process.env, 'env_var'],
398
+ [localStableConfig, 'local_stable_config'],
399
+ ]
400
+ for (const [source, sourceName] of sources) {
401
+ if (source !== undefined) {
402
+ const fromSource = getEnvNameFromSource(name, source)
403
+ if (fromSource !== undefined &&
404
+ parseConfigurationValue(fromSource, source[fromSource], sourceName, false) !== undefined) {
405
+ return fromSource
406
+ }
407
+ }
408
+ }
409
+ },
379
410
  }
@@ -124,6 +124,8 @@ const changeTracker = {
124
124
  */
125
125
  function undo (config, source) {
126
126
  for (const name of changeTracker[source]) {
127
+ if (trackedConfigOrigins.get(name) !== source) continue
128
+
127
129
  const entry = changeTracker.baseValuesByPath[name] ?? { source: 'default', value: defaults[name] }
128
130
  setAndTrack(config, name, entry.value, undefined, entry.source)
129
131
  }
@@ -248,9 +250,9 @@ class Config extends ConfigBase {
248
250
  const { experimental, ...rest } = options
249
251
  if (experimental) {
250
252
  // @ts-expect-error - Difficult to type this correctly.
251
- this.#applyOptions(experimental, 'code', 'experimental')
253
+ this.#applyOptions(experimental, 'experimental')
252
254
  }
253
- this.#applyOptions(rest, 'code')
255
+ this.#applyOptions(rest)
254
256
  this.#applyCalculated()
255
257
 
256
258
  warnWrongOtelSettings()
@@ -280,10 +282,9 @@ class Config extends ConfigBase {
280
282
 
281
283
  /**
282
284
  * @param {TracerOptions} options
283
- * @param {'code'} source
284
285
  * @param {string} [root]
285
286
  */
286
- #applyOptions (options, source, root = '') {
287
+ #applyOptions (options, root = '') {
287
288
  for (const [name, value] of Object.entries(options)) {
288
289
  const fullName = root ? `${root}.${name}` : name
289
290
  let entry = optionsTable[fullName]
@@ -300,7 +301,7 @@ class Config extends ConfigBase {
300
301
  // Defining an option that receives an object as value may not contain a property that is also
301
302
  // potentially a nested property!
302
303
  if (Object.hasOwn(value, nestedProperty)) {
303
- this.#applyOptions(value, source, fullName)
304
+ this.#applyOptions(value, fullName)
304
305
  matched = true
305
306
  break
306
307
  }
@@ -316,7 +317,7 @@ class Config extends ConfigBase {
316
317
  // TracePropagationStyle is special. It is a single option that is used to set both inject and extract.
317
318
  // TODO: Consider what to do with this later
318
319
  // @ts-expect-error - Difficult to type this correctly.
319
- this.#applyOptions({ inject: value, extract: value }, source, 'tracePropagationStyle')
320
+ this.#applyOptions({ inject: value, extract: value }, 'tracePropagationStyle')
320
321
  } else {
321
322
  log.warn('Unknown option %s with value %o', fullName, value)
322
323
  }
@@ -325,12 +326,12 @@ class Config extends ConfigBase {
325
326
  }
326
327
  const coerced = programmaticTypeCoercions[entry.type](value)
327
328
  if (coerced === undefined && value !== undefined) {
328
- warnInvalidValue(value, fullName, source, `Invalid ${entry.type} input`)
329
+ warnInvalidValue(value, fullName, 'code', `Invalid ${entry.type} input`)
329
330
  }
330
331
  const transformed = coerced !== undefined && entry.transformer
331
- ? entry.transformer(coerced, fullName, source)
332
+ ? entry.transformer(coerced, fullName, 'code')
332
333
  : coerced
333
- setAndTrack(this, entry.property ?? name, transformed, value, source)
334
+ setAndTrack(this, entry.property ?? name, transformed, value, 'code')
334
335
  }
335
336
  }
336
337
 
@@ -11,7 +11,7 @@ const MAX_SERVICE_LENGTH = 100
11
11
  * `_scope/name` in runtime metrics. Pre-normalizing pins both consumers
12
12
  * (and telemetry / process tags) to the same value.
13
13
  *
14
- * @see https://github.com/DataDog/datadog-agent/blob/main/pkg/trace/traceutil/normalize.go
14
+ * @see https://github.com/DataDog/datadog-agent/blob/main/pkg/trace/traceutil/normalize/normalize.go
15
15
  * @param {string | undefined} name
16
16
  */
17
17
  function normalizeService (name) {
@@ -227,12 +227,12 @@ const transformers = {
227
227
  if (url?.protocol === 'http:' || url?.protocol === 'https:') return value
228
228
  warnInvalidValue(value, optionName, source, 'Invalid HTTP URL')
229
229
  },
230
- validatePropagationStyles (value, optionName) {
230
+ validatePropagationStyles (value, optionName, _source, reportInvalid = true) {
231
231
  value = transformers.toLowerCase(value)
232
232
  for (let index = 0; index < value.length; index++) {
233
233
  const propagator = value[index]
234
234
  if (!VALID_PROPAGATION_STYLES.has(propagator)) {
235
- warnInvalidValue(propagator, optionName, optionName, 'Invalid propagator')
235
+ if (reportInvalid) warnInvalidValue(propagator, optionName, optionName, 'Invalid propagator')
236
236
  return
237
237
  }
238
238
  if (DD_MAJOR >= 6 && propagator === 'b3 single header') {
@@ -508,6 +508,14 @@
508
508
  "default": null
509
509
  }
510
510
  ],
511
+ "DD_BUNDLER_INJECT_GIT_METADATA": [
512
+ {
513
+ "implementation": "A",
514
+ "type": "boolean",
515
+ "default": "true",
516
+ "description": "When using the Datadog esbuild or webpack plugin, inject DD_GIT_COMMIT_SHA and DD_GIT_REPOSITORY_URL into the bundle. Set to false for stable artifact hashes across commits."
517
+ }
518
+ ],
511
519
  "DD_CIVISIBILITY_AGENTLESS_ENABLED": [
512
520
  {
513
521
  "implementation": "A",
@@ -937,6 +945,17 @@
937
945
  "default": "false"
938
946
  }
939
947
  ],
948
+ "DD_FEATURE_FLAGS_EVALUATION_TELEMETRY_PER_SUBJECT_OPT_OUT_ENABLED": [
949
+ {
950
+ "implementation": "A",
951
+ "type": "boolean",
952
+ "namespace": "featureFlags",
953
+ "configurationNames": [
954
+ "featureFlags.enableEvaluationTelemetryOptOutParameter"
955
+ ],
956
+ "default": "false"
957
+ }
958
+ ],
940
959
  "DD_FEATURE_FLAGS_CONFIGURATION_SOURCE": [
941
960
  {
942
961
  "implementation": "A",
@@ -2130,6 +2149,22 @@
2130
2149
  "namespace": "testOptimization"
2131
2150
  }
2132
2151
  ],
2152
+ "_DD_TEST_PARALLELIZATION_ENABLED": [
2153
+ {
2154
+ "implementation": "A",
2155
+ "type": "boolean",
2156
+ "default": null,
2157
+ "description": "Internal: Identifies test processes started by ddtest run."
2158
+ }
2159
+ ],
2160
+ "_DD_TEST_PARALLELIZATION_METADATA_FILE": [
2161
+ {
2162
+ "implementation": "A",
2163
+ "type": "string",
2164
+ "default": null,
2165
+ "description": "Internal: Absolute path to planning metadata supplied by ddtest run."
2166
+ }
2167
+ ],
2133
2168
  "DD_TEST_SESSION_NAME": [
2134
2169
  {
2135
2170
  "implementation": "A",
@@ -28,8 +28,9 @@ class StatsPoint {
28
28
  }
29
29
 
30
30
  addLatencies (checkpoint) {
31
- const edgeLatencySec = checkpoint.edgeLatencyNs / 1e9
32
- const pathwayLatencySec = checkpoint.pathwayLatencyNs / 1e9
31
+ // Start times can come from another host's clock, so skew can make these negative.
32
+ const edgeLatencySec = Math.max(0, checkpoint.edgeLatencyNs / 1e9)
33
+ const pathwayLatencySec = Math.max(0, checkpoint.pathwayLatencyNs / 1e9)
33
34
  this.edgeLatency.accept(edgeLatencySec)
34
35
  this.pathwayLatency.accept(pathwayLatencySec)
35
36
  this.payloadSize.accept(checkpoint.payloadSize)
@@ -54,10 +54,7 @@ class SchemaBuilder {
54
54
  }
55
55
 
56
56
  shouldExtractSchema (schemaName, depth) {
57
- if (depth > maxDepth) {
58
- return false
59
- }
60
- if (schemaName in this.schema.components.schemas) {
57
+ if (depth > maxDepth || schemaName in this.schema.components.schemas) {
61
58
  return false
62
59
  }
63
60
  this.schema.components.schemas[schemaName] = new OpenApiSchema.SCHEMA()
@@ -61,6 +61,7 @@ function send (message, logger, dd, snapshot, processTags, eventType, incomplete
61
61
  ddsource,
62
62
  hostname,
63
63
  service,
64
+ timestamp: snapshot.timestamp,
64
65
  message,
65
66
  logger,
66
67
  dd,