dd-trace 6.8.0 → 6.10.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 (209) hide show
  1. package/ci/diagnose.js +27 -25
  2. package/ci/init.js +6 -6
  3. package/ci/runbook.md +114 -20
  4. package/ci/test-optimization-validation/approval-artifacts.js +36 -3
  5. package/ci/test-optimization-validation/approval.js +78 -22
  6. package/ci/test-optimization-validation/blocker-category.js +24 -0
  7. package/ci/test-optimization-validation/ci-discovery.js +23 -17
  8. package/ci/test-optimization-validation/ci-package-scripts.js +34 -6
  9. package/ci/test-optimization-validation/ci-remediation.js +1 -1
  10. package/ci/test-optimization-validation/cli.js +280 -52
  11. package/ci/test-optimization-validation/command-blocker.js +159 -20
  12. package/ci/test-optimization-validation/command-output-policy.js +9 -3
  13. package/ci/test-optimization-validation/command-runner.js +9 -7
  14. package/ci/test-optimization-validation/environment.js +4 -2
  15. package/ci/test-optimization-validation/executable.js +15 -13
  16. package/ci/test-optimization-validation/execution-lock.js +70 -0
  17. package/ci/test-optimization-validation/framework-adapters/cucumber-validation.json +3 -0
  18. package/ci/test-optimization-validation/framework-adapters/cucumber.js +657 -5
  19. package/ci/test-optimization-validation/framework-adapters/vitest.js +517 -0
  20. package/ci/test-optimization-validation/generated-files.js +38 -28
  21. package/ci/test-optimization-validation/generated-test-contract.js +8 -4
  22. package/ci/test-optimization-validation/generated-verifier.js +24 -17
  23. package/ci/test-optimization-validation/literal-glob.js +52 -0
  24. package/ci/test-optimization-validation/manifest-loader.js +3 -0
  25. package/ci/test-optimization-validation/manifest-scaffold.js +300 -50
  26. package/ci/test-optimization-validation/manifest-schema.js +80 -17
  27. package/ci/test-optimization-validation/offline-fixtures.js +13 -4
  28. package/ci/test-optimization-validation/package-check.js +94 -0
  29. package/ci/test-optimization-validation/plan-writer.js +183 -10
  30. package/ci/test-optimization-validation/preflight-runner.js +110 -31
  31. package/ci/test-optimization-validation/project-build-artifact.js +31 -0
  32. package/ci/test-optimization-validation/redaction.js +18 -27
  33. package/ci/test-optimization-validation/report-writer.js +182 -48
  34. package/ci/test-optimization-validation/result-semantics.js +20 -3
  35. package/ci/test-optimization-validation/runner-command.js +40 -20
  36. package/ci/test-optimization-validation/runner-contract.js +79 -43
  37. package/ci/test-optimization-validation/scenarios/basic-reporting.js +59 -5
  38. package/ci/test-optimization-validation/scenarios/ci-wiring.js +141 -35
  39. package/ci/test-optimization-validation/scenarios/helpers.js +30 -6
  40. package/ci/test-optimization-validation/static-diagnosis.js +10 -3
  41. package/ci/test-optimization-validation/test-output.js +2 -1
  42. package/ci/test-optimization-validation/validation-blocker.js +21 -0
  43. package/ci/vitest-no-worker-init-setup.mjs +140 -42
  44. package/index.d.ts +140 -1
  45. package/init.js +1 -17
  46. package/initialize.mjs +11 -0
  47. package/loader-hook.mjs +6 -4
  48. package/package.json +8 -5
  49. package/packages/datadog-core/src/utils/src/parse-tags.js +0 -1
  50. package/packages/datadog-esbuild/src/utils.js +16 -1
  51. package/packages/datadog-instrumentations/src/ai.js +61 -34
  52. package/packages/datadog-instrumentations/src/bluebird.js +6 -6
  53. package/packages/datadog-instrumentations/src/claude-agent-sdk.js +81 -5
  54. package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +111 -6
  55. package/packages/datadog-instrumentations/src/cucumber.js +124 -47
  56. package/packages/datadog-instrumentations/src/dns.js +21 -2
  57. package/packages/datadog-instrumentations/src/electron.js +1 -0
  58. package/packages/datadog-instrumentations/src/fastify.js +3 -1
  59. package/packages/datadog-instrumentations/src/grpc/client.js +4 -2
  60. package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +3 -1
  61. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
  62. package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +629 -0
  63. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +34 -9
  64. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +199 -0
  65. package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +20 -2
  66. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.js +32 -0
  67. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +62 -0
  68. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +83 -1
  69. package/packages/datadog-instrumentations/src/helpers/router-helper.js +37 -6
  70. package/packages/datadog-instrumentations/src/jest/coverage-backfill.js +1 -1
  71. package/packages/datadog-instrumentations/src/jest.js +980 -234
  72. package/packages/datadog-instrumentations/src/mocha/main.js +108 -10
  73. package/packages/datadog-instrumentations/src/mocha/utils.js +66 -50
  74. package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +25 -0
  75. package/packages/datadog-instrumentations/src/mocha/worker.js +12 -18
  76. package/packages/datadog-instrumentations/src/mysql.js +46 -16
  77. package/packages/datadog-instrumentations/src/mysql2.js +119 -18
  78. package/packages/datadog-instrumentations/src/next.js +155 -23
  79. package/packages/datadog-instrumentations/src/openai.js +14 -0
  80. package/packages/datadog-instrumentations/src/path-to-regexp.js +67 -1
  81. package/packages/datadog-instrumentations/src/pg.js +209 -11
  82. package/packages/datadog-instrumentations/src/playwright.js +173 -79
  83. package/packages/datadog-instrumentations/src/promise-js.js +4 -4
  84. package/packages/datadog-instrumentations/src/promise.js +3 -3
  85. package/packages/datadog-instrumentations/src/router.js +195 -19
  86. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +85 -13
  87. package/packages/datadog-instrumentations/src/vitest-main.js +528 -72
  88. package/packages/datadog-instrumentations/src/vitest-util.js +106 -12
  89. package/packages/datadog-instrumentations/src/vitest-worker.js +382 -40
  90. package/packages/datadog-instrumentations/src/webdriverio.js +212 -22
  91. package/packages/datadog-instrumentations/src/when.js +3 -3
  92. package/packages/datadog-instrumentations/src/ws.js +5 -1
  93. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/trace-checkpoint.js +12 -6
  94. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +59 -48
  95. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +2 -5
  96. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -5
  97. package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +1 -3
  98. package/packages/datadog-plugin-cucumber/src/index.js +5 -1
  99. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +51 -47
  100. package/packages/datadog-plugin-cypress/src/support.js +7 -4
  101. package/packages/datadog-plugin-electron/src/net.js +10 -4
  102. package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +31 -17
  103. package/packages/datadog-plugin-http/src/client.js +1 -1
  104. package/packages/datadog-plugin-http2/src/client.js +1 -1
  105. package/packages/datadog-plugin-jest/src/index.js +6 -22
  106. package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +2 -3
  107. package/packages/datadog-plugin-kafkajs/src/consumer.js +2 -3
  108. package/packages/datadog-plugin-kafkajs/src/producer.js +5 -3
  109. package/packages/datadog-plugin-kafkajs/src/utils.js +65 -7
  110. package/packages/datadog-plugin-mocha/src/index.js +832 -4
  111. package/packages/datadog-plugin-mysql/src/index.js +37 -0
  112. package/packages/datadog-plugin-next/src/index.js +51 -21
  113. package/packages/datadog-plugin-openai/src/tracing.js +4 -0
  114. package/packages/datadog-plugin-openai-agents/src/integration.js +278 -69
  115. package/packages/datadog-plugin-openai-agents/src/processor.js +16 -4
  116. package/packages/datadog-plugin-pg/src/index.js +66 -1
  117. package/packages/datadog-plugin-playwright/src/index.js +1 -1
  118. package/packages/datadog-plugin-vitest/src/index.js +103 -27
  119. package/packages/datadog-plugin-ws/src/util.js +2 -1
  120. package/packages/datadog-shimmer/src/shimmer.js +6 -2
  121. package/packages/dd-trace/src/aiguard/sdk.js +3 -0
  122. package/packages/dd-trace/src/appsec/api_security/normalized-route.js +453 -0
  123. package/packages/dd-trace/src/appsec/{blocking.js → blocking/index.js} +26 -6
  124. package/packages/dd-trace/src/appsec/handlers/auth.js +62 -0
  125. package/packages/dd-trace/src/appsec/handlers/http-request.js +228 -0
  126. package/packages/dd-trace/src/appsec/handlers/http-response.js +78 -0
  127. package/packages/dd-trace/src/appsec/handlers/http-shared.js +24 -0
  128. package/packages/dd-trace/src/appsec/handlers/payments.js +104 -0
  129. package/packages/dd-trace/src/appsec/iast/analyzers/hsts-header-missing-analyzer.js +1 -1
  130. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +1 -1
  131. package/packages/dd-trace/src/appsec/index.js +48 -476
  132. package/packages/dd-trace/src/appsec/sdk/track_event.js +9 -0
  133. package/packages/dd-trace/src/carrier.js +356 -0
  134. package/packages/dd-trace/src/ci-visibility/coverage-report-discovery.js +46 -14
  135. package/packages/dd-trace/src/ci-visibility/efd-retry-policy.js +89 -0
  136. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +27 -13
  137. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +17 -0
  138. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +21 -1
  139. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +22 -7
  140. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +9 -14
  141. package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +21 -5
  142. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +1 -1
  143. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +5 -1
  144. package/packages/dd-trace/src/config/generated-config-types.d.ts +5 -0
  145. package/packages/dd-trace/src/config/helper.js +2 -0
  146. package/packages/dd-trace/src/config/index.js +16 -5
  147. package/packages/dd-trace/src/config/remote_config.js +1 -1
  148. package/packages/dd-trace/src/config/supported-configurations.json +18 -0
  149. package/packages/dd-trace/src/datastreams/context.js +6 -1
  150. package/packages/dd-trace/src/datastreams/manager.js +5 -1
  151. package/packages/dd-trace/src/datastreams/pathway.js +23 -22
  152. package/packages/dd-trace/src/datastreams/processor.js +3 -2
  153. package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -1
  154. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +0 -1
  155. package/packages/dd-trace/src/guardrails/apply-pm2-env.js +16 -0
  156. package/packages/dd-trace/src/guardrails/log.js +1 -10
  157. package/packages/dd-trace/src/llmobs/constants/tags.js +5 -0
  158. package/packages/dd-trace/src/llmobs/eval-metric.js +249 -0
  159. package/packages/dd-trace/src/llmobs/experiments/index.js +33 -25
  160. package/packages/dd-trace/src/llmobs/experiments/util.js +4 -2
  161. package/packages/dd-trace/src/llmobs/index.js +34 -2
  162. package/packages/dd-trace/src/llmobs/noop.js +2 -0
  163. package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +1 -1
  164. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +1 -1
  165. package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +5 -5
  166. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +1 -1
  167. package/packages/dd-trace/src/llmobs/sdk.js +149 -68
  168. package/packages/dd-trace/src/llmobs/span_processor.js +16 -0
  169. package/packages/dd-trace/src/llmobs/tagger.js +96 -0
  170. package/packages/dd-trace/src/llmobs/telemetry.js +18 -0
  171. package/packages/dd-trace/src/llmobs/util.js +78 -0
  172. package/packages/dd-trace/src/llmobs/writers/base.js +3 -0
  173. package/packages/dd-trace/src/llmobs/writers/util.js +2 -0
  174. package/packages/dd-trace/src/log/channels.js +1 -9
  175. package/packages/dd-trace/src/log/levels.js +12 -0
  176. package/packages/dd-trace/src/openfeature/writers/exposures.js +1 -1
  177. package/packages/dd-trace/src/openfeature/writers/util.js +1 -1
  178. package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +1 -1
  179. package/packages/dd-trace/src/opentelemetry/metrics/meter.js +8 -4
  180. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +1 -1
  181. package/packages/dd-trace/src/opentelemetry/span.js +4 -0
  182. package/packages/dd-trace/src/opentracing/propagation/text_map.js +126 -120
  183. package/packages/dd-trace/src/opentracing/propagation/text_map_dsm.js +4 -8
  184. package/packages/dd-trace/src/plugin_manager.js +2 -0
  185. package/packages/dd-trace/src/plugins/ci_plugin.js +21 -5
  186. package/packages/dd-trace/src/plugins/index.js +1 -0
  187. package/packages/dd-trace/src/plugins/util/ci.js +6 -0
  188. package/packages/dd-trace/src/plugins/util/git.js +6 -4
  189. package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +2 -2
  190. package/packages/dd-trace/src/plugins/util/ip_extractor.js +1 -1
  191. package/packages/{datadog-plugin-jest/src/util.js → dd-trace/src/plugins/util/jest.js} +3 -62
  192. package/packages/dd-trace/src/plugins/util/stacktrace.js +3 -0
  193. package/packages/dd-trace/src/plugins/util/test.js +397 -74
  194. package/packages/dd-trace/src/plugins/util/url.js +1 -1
  195. package/packages/dd-trace/src/plugins/util/web.js +76 -3
  196. package/packages/dd-trace/src/profiler.js +1 -1
  197. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +1 -1
  198. package/packages/dd-trace/src/profiling/profilers/events.js +2 -2
  199. package/packages/dd-trace/src/proxy.js +1 -0
  200. package/packages/dd-trace/src/ritm.js +5 -0
  201. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +4 -2
  202. package/packages/dd-trace/src/sampling_rule.js +4 -2
  203. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +1 -1
  204. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +1 -1
  205. package/packages/dd-trace/src/telemetry/logs/index.js +3 -0
  206. package/packages/dd-trace/src/telemetry/telemetry.js +1 -1
  207. package/vendor/dist/pprof-format/index.js +1 -1
  208. package/version.js +10 -8
  209. /package/packages/dd-trace/src/appsec/{blocked_templates.js → blocking/templates.js} +0 -0
@@ -487,16 +487,18 @@ function extractMessagesFromConverseContent (role, contentBlocks) {
487
487
  const toolCalls = []
488
488
  const toolResults = []
489
489
 
490
- for (const block of contentBlocks || []) {
491
- if (block == null || typeof block !== 'object') continue
492
- if (typeof block.text === 'string') {
493
- content += block.text
494
- } else if (block.toolUse) {
495
- toolCalls.push(buildToolCall(block.toolUse))
496
- } else if (block.toolResult) {
497
- toolResults.push(buildToolResult(block.toolResult))
498
- } else {
499
- content += `[Unsupported content type: ${getContentBlockType(block)}]`
490
+ if (contentBlocks) {
491
+ for (const block of contentBlocks) {
492
+ if (block == null || typeof block !== 'object') continue
493
+ if (typeof block.text === 'string') {
494
+ content += block.text
495
+ } else if (block.toolUse) {
496
+ toolCalls.push(buildToolCall(block.toolUse))
497
+ } else if (block.toolResult) {
498
+ toolResults.push(buildToolResult(block.toolResult))
499
+ } else {
500
+ content += `[Unsupported content type: ${getContentBlockType(block)}]`
501
+ }
500
502
  }
501
503
  }
502
504
 
@@ -572,14 +574,17 @@ function buildUsage (usage = {}) {
572
574
  */
573
575
  function extractConverseToolDefinitions (params) {
574
576
  const toolDefinitions = []
575
- for (const tool of params.toolConfig?.tools || []) {
576
- const toolSpec = tool?.toolSpec
577
- if (!toolSpec?.name) continue
578
- toolDefinitions.push({
579
- name: toolSpec.name,
580
- description: toolSpec.description ?? '',
581
- schema: toolSpec.inputSchema ?? {},
582
- })
577
+ const tools = params.toolConfig?.tools
578
+ if (tools) {
579
+ for (const tool of tools) {
580
+ const toolSpec = tool?.toolSpec
581
+ if (!toolSpec?.name) continue
582
+ toolDefinitions.push({
583
+ name: toolSpec.name,
584
+ description: toolSpec.description ?? '',
585
+ schema: toolSpec.inputSchema ?? {},
586
+ })
587
+ }
583
588
  }
584
589
  return toolDefinitions
585
590
  }
@@ -593,13 +598,17 @@ function extractConverseToolDefinitions (params) {
593
598
  */
594
599
  function extractRequestParamsConverse (params) {
595
600
  const prompt = []
596
- for (const block of params.system || []) {
597
- if (typeof block?.text === 'string') prompt.push({ content: block.text, role: 'system' })
601
+ if (params.system) {
602
+ for (const block of params.system) {
603
+ if (typeof block?.text === 'string') prompt.push({ content: block.text, role: 'system' })
604
+ }
598
605
  }
599
- for (const msg of params.messages || []) {
600
- if (msg == null || typeof msg !== 'object') continue
601
- const message = extractMessagesFromConverseContent(msg.role || 'user', msg.content)
602
- if (message) prompt.push(message)
606
+ if (params.messages) {
607
+ for (const msg of params.messages) {
608
+ if (msg == null || typeof msg !== 'object') continue
609
+ const message = extractMessagesFromConverseContent(msg.role || 'user', msg.content)
610
+ if (message) prompt.push(message)
611
+ }
603
612
  }
604
613
 
605
614
  const { temperature, topP, maxTokens, stopSequences } = params.inferenceConfig || {}
@@ -632,7 +641,7 @@ function extractTextAndResponseReasonConverse (response) {
632
641
  * response, spread across start/delta chunks. We reassemble those chunks
633
642
  * into a normalized content-block array and reuse the non-stream extractor.
634
643
  *
635
- * @param {Array<object>} chunks - Ordered ConverseStreamOutput events.
644
+ * @param {Array<object> | undefined} chunks - Ordered ConverseStreamOutput events.
636
645
  * @returns {Generation}
637
646
  */
638
647
  function extractTextAndResponseReasonConverseFromStream (chunks) {
@@ -641,29 +650,31 @@ function extractTextAndResponseReasonConverseFromStream (chunks) {
641
650
  let usage = {}
642
651
  const blocksByIdx = new Map()
643
652
 
644
- for (const chunk of chunks || []) {
645
- if (chunk.messageStart?.role) {
646
- role = chunk.messageStart.role
647
- } else if (chunk.messageStop?.stopReason) {
648
- stopReason = chunk.messageStop.stopReason
649
- } else if (chunk.metadata?.usage) {
650
- usage = chunk.metadata.usage
651
- } else if (chunk.contentBlockStart?.start?.toolUse) {
652
- const { contentBlockIndex, start: { toolUse } } = chunk.contentBlockStart
653
- blocksByIdx.set(contentBlockIndex, {
654
- toolUse: { toolUseId: toolUse.toolUseId, name: toolUse.name, inputStr: '' },
655
- })
656
- } else if (chunk.contentBlockDelta) {
657
- const { contentBlockIndex, delta } = chunk.contentBlockDelta
658
- if (typeof delta?.text === 'string') {
659
- const block = blocksByIdx.get(contentBlockIndex) ?? {}
660
- block.text = (block.text ?? '') + delta.text
661
- blocksByIdx.set(contentBlockIndex, block)
662
- } else if (typeof delta?.toolUse?.input === 'string') {
663
- const block = blocksByIdx.get(contentBlockIndex) ?? { toolUse: { inputStr: '' } }
664
- block.toolUse ??= { inputStr: '' }
665
- block.toolUse.inputStr += delta.toolUse.input
666
- blocksByIdx.set(contentBlockIndex, block)
653
+ if (chunks) {
654
+ for (const chunk of chunks) {
655
+ if (chunk.messageStart?.role) {
656
+ role = chunk.messageStart.role
657
+ } else if (chunk.messageStop?.stopReason) {
658
+ stopReason = chunk.messageStop.stopReason
659
+ } else if (chunk.metadata?.usage) {
660
+ usage = chunk.metadata.usage
661
+ } else if (chunk.contentBlockStart?.start?.toolUse) {
662
+ const { contentBlockIndex, start: { toolUse } } = chunk.contentBlockStart
663
+ blocksByIdx.set(contentBlockIndex, {
664
+ toolUse: { toolUseId: toolUse.toolUseId, name: toolUse.name, inputStr: '' },
665
+ })
666
+ } else if (chunk.contentBlockDelta) {
667
+ const { contentBlockIndex, delta } = chunk.contentBlockDelta
668
+ if (typeof delta?.text === 'string') {
669
+ const block = blocksByIdx.get(contentBlockIndex) ?? {}
670
+ block.text = (block.text ?? '') + delta.text
671
+ blocksByIdx.set(contentBlockIndex, block)
672
+ } else if (typeof delta?.toolUse?.input === 'string') {
673
+ const block = blocksByIdx.get(contentBlockIndex) ?? { toolUse: { inputStr: '' } }
674
+ block.toolUse ??= { inputStr: '' }
675
+ block.toolUse.inputStr += delta.toolUse.input
676
+ blocksByIdx.set(contentBlockIndex, block)
677
+ }
667
678
  }
668
679
  }
669
680
  }
@@ -196,14 +196,11 @@ class Kinesis extends BaseAwsSdkPlugin {
196
196
  try {
197
197
  parsedAttributes = JSON.parse(recordDataAsString(record.Data))
198
198
  } catch {
199
- // Non-JSON record. Skip DSM context for this entry; the
200
- // checkpoint payload size below is still reported.
199
+ // Non-JSON record: it carries no context, so the checkpoint below starts a new pathway.
201
200
  }
202
201
 
203
202
  const payloadSize = getSizeOrZero(record.Data)
204
- if (parsedAttributes?._datadog) {
205
- this.tracer.decodeDataStreamsContext(parsedAttributes._datadog)
206
- }
203
+ this.tracer.decodeDataStreamsContext(parsedAttributes?._datadog)
207
204
  this.tracer.setCheckpoint(tags, span, payloadSize)
208
205
  }
209
206
  }
@@ -275,11 +275,7 @@ class Sqs extends BaseAwsSdkPlugin {
275
275
  for (let i = 0; i < messages.length; i++) {
276
276
  const message = messages[i]
277
277
  const carrier = carriers === undefined ? this.parseMessageCarrier(message) : carriers[i]
278
- if (carrier) {
279
- // Inert for EventBridge until its producer emits a pathway (separate
280
- // change) — no `dd-pathway-ctx-base64` to decode yet; SQS/SNS decode now.
281
- this.tracer.decodeDataStreamsContext(carrier)
282
- }
278
+ this.tracer.decodeDataStreamsContext(carrier)
283
279
  const payloadSize = getHeadersSize({
284
280
  Body: message.Body,
285
281
  MessageAttributes: message.MessageAttributes,
@@ -58,9 +58,7 @@ function scrubChildProcessCmd (expression) {
58
58
  for (let index = 0; index < expressionTokens.length; index++) {
59
59
  const token = expressionTokens[index]
60
60
 
61
- if (token === null) {
62
- continue
63
- } else if (typeof token === 'object') {
61
+ if (token !== null && typeof token === 'object') {
64
62
  if (token.pattern) {
65
63
  result.push(token.pattern)
66
64
  } else if (token.op) {
@@ -308,6 +308,7 @@ class CucumberPlugin extends CiPlugin {
308
308
  isNew,
309
309
  isEfdRetry,
310
310
  isFlakyRetry,
311
+ isExternalRetry,
311
312
  isAttemptToFix,
312
313
  isAttemptToFixRetry,
313
314
  hasFailedAllRetries,
@@ -348,9 +349,12 @@ class CucumberPlugin extends CiPlugin {
348
349
  span.setTag(ERROR_MESSAGE, errorMessage)
349
350
  }
350
351
 
351
- if (isFlakyRetry > 0) {
352
+ if (isFlakyRetry) {
352
353
  span.setTag(TEST_IS_RETRY, 'true')
353
354
  span.setTag(TEST_RETRY_REASON, TEST_RETRY_REASON_TYPES.atr)
355
+ } else if (isExternalRetry) {
356
+ span.setTag(TEST_IS_RETRY, 'true')
357
+ span.setTag(TEST_RETRY_REASON, TEST_RETRY_REASON_TYPES.ext)
354
358
  }
355
359
 
356
360
  if (hasFailedAllRetries) {
@@ -8,6 +8,12 @@ const dateNow = Date.now
8
8
  const { createCoverageMap } = require('../../../vendor/dist/istanbul-lib-coverage')
9
9
  const satisfies = require('../../../vendor/dist/semifies')
10
10
  const { RUM_TEST_EXECUTION_ID_COOKIE_NAME } = require('../../dd-trace/src/ci-visibility/rum')
11
+ const {
12
+ EMPTY_EFD_RETRY_POLICY,
13
+ getEfdRetryCountForDuration,
14
+ hasEfdRetries,
15
+ shouldSkipEfdRetry,
16
+ } = require('../../dd-trace/src/ci-visibility/efd-retry-policy')
11
17
  const {
12
18
  TEST_STATUS,
13
19
  setRumTestTags,
@@ -72,16 +78,15 @@ const {
72
78
  TEST_HAS_DYNAMIC_NAME,
73
79
  getIsFaultyEarlyFlakeDetection,
74
80
  DYNAMIC_NAME_RE,
81
+ isMarkedAsUnskippable,
75
82
  recordAttemptToFixExecution,
83
+ recordTestManagementExecution,
76
84
  logAttemptToFixTestExecution,
77
85
  logTestOptimizationSummary,
78
- getEfdRetryCount,
79
- getMaxEfdRetryCount,
80
86
  getPullRequestBaseBranch,
81
87
  TEST_FINAL_STATUS,
82
88
  getTestOptimizationRequestResults,
83
89
  } = require('../../dd-trace/src/plugins/util/test')
84
- const { isMarkedAsUnskippable } = require('../../datadog-plugin-jest/src/util')
85
90
  const { ORIGIN_KEY, COMPONENT } = require('../../dd-trace/src/constants')
86
91
  const { RESOURCE_NAME } = require('../../../ext/tags')
87
92
  const getConfig = require('../../dd-trace/src/config')
@@ -465,8 +470,7 @@ class CypressPlugin {
465
470
  isEarlyFlakeDetectionEnabled = false
466
471
  isEarlyFlakeDetectionFaulty = false
467
472
  isKnownTestsEnabled = false
468
- earlyFlakeDetectionNumRetries = 0
469
- earlyFlakeDetectionSlowTestRetries = {}
473
+ earlyFlakeDetectionRetryPolicy = EMPTY_EFD_RETRY_POLICY
470
474
  efdRetryCountByTest = {}
471
475
  efdSlowAbortedTests = {}
472
476
  earlyFlakeDetectionFaultyThreshold = 0
@@ -554,8 +558,7 @@ class CypressPlugin {
554
558
  this.isEarlyFlakeDetectionEnabled = false
555
559
  this.isEarlyFlakeDetectionFaulty = false
556
560
  this.isKnownTestsEnabled = false
557
- this.earlyFlakeDetectionNumRetries = 0
558
- this.earlyFlakeDetectionSlowTestRetries = {}
561
+ this.earlyFlakeDetectionRetryPolicy = EMPTY_EFD_RETRY_POLICY
559
562
  this.efdRetryCountByTest = {}
560
563
  this.efdSlowAbortedTests = {}
561
564
  this.earlyFlakeDetectionFaultyThreshold = 0
@@ -835,8 +838,7 @@ class CypressPlugin {
835
838
  isCodeCoverageEnabled,
836
839
  isCoverageReportUploadEnabled,
837
840
  isEarlyFlakeDetectionEnabled,
838
- earlyFlakeDetectionNumRetries,
839
- earlyFlakeDetectionSlowTestRetries,
841
+ earlyFlakeDetectionRetryPolicy,
840
842
  earlyFlakeDetectionFaultyThreshold,
841
843
  isFlakyTestRetriesEnabled,
842
844
  flakyTestRetriesCount,
@@ -851,8 +853,7 @@ class CypressPlugin {
851
853
  this.isCodeCoverageEnabled = isCodeCoverageEnabled
852
854
  this.isCoverageReportUploadEnabled = isCoverageReportUploadEnabled
853
855
  this.isEarlyFlakeDetectionEnabled = isEarlyFlakeDetectionEnabled
854
- this.earlyFlakeDetectionNumRetries = earlyFlakeDetectionNumRetries
855
- this.earlyFlakeDetectionSlowTestRetries = earlyFlakeDetectionSlowTestRetries ?? {}
856
+ this.earlyFlakeDetectionRetryPolicy = earlyFlakeDetectionRetryPolicy ?? EMPTY_EFD_RETRY_POLICY
856
857
  this.earlyFlakeDetectionFaultyThreshold = earlyFlakeDetectionFaultyThreshold
857
858
  this.isKnownTestsEnabled = isKnownTestsEnabled
858
859
  if (isFlakyTestRetriesEnabled && this.isTestIsolationEnabled) {
@@ -900,34 +901,6 @@ class CypressPlugin {
900
901
  return { isAttemptToFix, isDisabled, isQuarantined }
901
902
  }
902
903
 
903
- /**
904
- * Returns how many EFD retries must be scheduled before the first duration is known.
905
- *
906
- * @returns {number}
907
- */
908
- getConfiguredEfdRetryCount () {
909
- const { earlyFlakeDetectionSlowTestRetries } = this
910
- if (!earlyFlakeDetectionSlowTestRetries || !Object.keys(earlyFlakeDetectionSlowTestRetries).length) {
911
- return this.earlyFlakeDetectionNumRetries
912
- }
913
- return getMaxEfdRetryCount(earlyFlakeDetectionSlowTestRetries)
914
- }
915
-
916
- /**
917
- * Returns the selected EFD retry count for a test, or the scheduling count if it has not run yet.
918
- *
919
- * @param {string} testSuite
920
- * @param {string} testName
921
- * @returns {number}
922
- */
923
- getEfdRetryCountForTest (testSuite, testName) {
924
- const testSuiteRetries = this.efdRetryCountByTest[testSuite]
925
- if (!testSuiteRetries || testSuiteRetries[testName] === undefined) {
926
- return this.getConfiguredEfdRetryCount()
927
- }
928
- return testSuiteRetries[testName]
929
- }
930
-
931
904
  /**
932
905
  * Stores the selected EFD retry count for a test after its first execution duration is known.
933
906
  *
@@ -940,7 +913,7 @@ class CypressPlugin {
940
913
  if (!this.efdRetryCountByTest[testSuite]) {
941
914
  this.efdRetryCountByTest[testSuite] = {}
942
915
  }
943
- const retryCount = getEfdRetryCount(duration ?? 0, this.earlyFlakeDetectionSlowTestRetries)
916
+ const retryCount = getEfdRetryCountForDuration(duration ?? 0, this.earlyFlakeDetectionRetryPolicy)
944
917
  this.efdRetryCountByTest[testSuite][testName] = retryCount
945
918
  if (retryCount === 0) {
946
919
  if (!this.efdSlowAbortedTests[testSuite]) {
@@ -961,7 +934,7 @@ class CypressPlugin {
961
934
  */
962
935
  shouldSkipEfdRetry (testSuite, testName, efdRetryIndex) {
963
936
  const testSuiteRetries = this.efdRetryCountByTest[testSuite]
964
- return testSuiteRetries?.[testName] !== undefined && efdRetryIndex > testSuiteRetries[testName]
937
+ return shouldSkipEfdRetry(efdRetryIndex, testSuiteRetries?.[testName])
965
938
  }
966
939
 
967
940
  getTestSuiteSpan ({ testSuite, testSuiteAbsolutePath }) {
@@ -1431,7 +1404,8 @@ class CypressPlugin {
1431
1404
  skippedTestSpan.setTag(ITR_CORRELATION_ID, this.itrCorrelationId)
1432
1405
  }
1433
1406
 
1434
- const { isDisabled, isQuarantined } = this.getTestProperties(spec.relative, cypressTestName)
1407
+ const { isAttemptToFix, isDisabled, isQuarantined } =
1408
+ this.getTestProperties(spec.relative, cypressTestName)
1435
1409
 
1436
1410
  if (isDisabled) {
1437
1411
  skippedTestSpan.setTag(TEST_MANAGEMENT_IS_DISABLED, 'true')
@@ -1439,6 +1413,24 @@ class CypressPlugin {
1439
1413
  skippedTestSpan.setTag(TEST_MANAGEMENT_IS_QUARANTINED, 'true')
1440
1414
  }
1441
1415
 
1416
+ recordTestManagementExecution({
1417
+ testSuite: spec.relative,
1418
+ testName: cypressTestName,
1419
+ status: 'skip',
1420
+ isAttemptToFix,
1421
+ isDisabled,
1422
+ isQuarantined,
1423
+ })
1424
+ if (isAttemptToFix) {
1425
+ recordAttemptToFixExecution(this.attemptToFixExecutions, {
1426
+ testSuite: spec.relative,
1427
+ testName: cypressTestName,
1428
+ status: 'skip',
1429
+ isDisabled,
1430
+ isQuarantined,
1431
+ })
1432
+ }
1433
+
1442
1434
  skippedTestSpan.finish()
1443
1435
  }
1444
1436
 
@@ -1522,6 +1514,15 @@ class CypressPlugin {
1522
1514
  finishedTest.testSpan.setTag(TEST_MANAGEMENT_ATTEMPT_TO_FIX_PASSED, 'false')
1523
1515
  }
1524
1516
  }
1517
+ const testManagementTags = finishedTest.testSpan.context().getTags()
1518
+ recordTestManagementExecution({
1519
+ testSuite: spec.relative,
1520
+ testName,
1521
+ status: testManagementTags[TEST_STATUS] || cypressTestStatus,
1522
+ isAttemptToFix: finishedTest.isAttemptToFix,
1523
+ isDisabled: testManagementTags[TEST_MANAGEMENT_IS_DISABLED] === 'true',
1524
+ isQuarantined: testManagementTags[TEST_MANAGEMENT_IS_QUARANTINED] === 'true',
1525
+ })
1525
1526
  if (this.itrCorrelationId) {
1526
1527
  finishedTest.testSpan.setTag(ITR_CORRELATION_ID, this.itrCorrelationId)
1527
1528
  }
@@ -1671,10 +1672,10 @@ class CypressPlugin {
1671
1672
  return {
1672
1673
  'dd:testSuiteStart': ({ testSuite, testSuiteAbsolutePath }) => {
1673
1674
  const suitePayload = {
1674
- isEarlyFlakeDetectionEnabled: this.isEarlyFlakeDetectionEnabled,
1675
+ isEarlyFlakeDetectionEnabled:
1676
+ this.isEarlyFlakeDetectionEnabled && hasEfdRetries(this.earlyFlakeDetectionRetryPolicy),
1675
1677
  knownTestsForSuite: this.knownTestsByTestSuite?.[testSuite] || [],
1676
- earlyFlakeDetectionNumRetries: this.getConfiguredEfdRetryCount(),
1677
- earlyFlakeDetectionSlowTestRetries: this.earlyFlakeDetectionSlowTestRetries,
1678
+ earlyFlakeDetectionSchedulingRetryCount: this.earlyFlakeDetectionRetryPolicy.schedulingRetryCount,
1678
1679
  isKnownTestsEnabled: this.isKnownTestsEnabled,
1679
1680
  isTestManagementEnabled: this.isTestManagementTestsEnabled,
1680
1681
  testManagementAttemptToFixRetries: this.testManagementAttemptToFixRetries,
@@ -1784,7 +1785,10 @@ class CypressPlugin {
1784
1785
  }
1785
1786
  this.tracer._tracer._exporter.exportCoverage(formattedCoverage)
1786
1787
  }
1787
- const isEfdManagedTest = (isNew || isModified) && this.isEarlyFlakeDetectionEnabled && !isAttemptToFix
1788
+ const isEfdManagedTest = (isNew || isModified) &&
1789
+ this.isEarlyFlakeDetectionEnabled &&
1790
+ hasEfdRetries(this.earlyFlakeDetectionRetryPolicy) &&
1791
+ !isAttemptToFix
1788
1792
  let testStatus = CYPRESS_STATUS_TO_TEST_STATUS[state]
1789
1793
  let didAbortSlowEfdRetries = false
1790
1794
  if (isEfdManagedTest && !isEfdRetry && this.efdRetryCountByTest[testSuite]?.[testName] === undefined) {
@@ -1858,7 +1862,7 @@ class CypressPlugin {
1858
1862
  }
1859
1863
  // Check if all EFD retries failed
1860
1864
  if (isEfdManagedTest) {
1861
- const efdRetryCount = this.getEfdRetryCountForTest(testSuite, testName)
1865
+ const efdRetryCount = this.efdRetryCountByTest[testSuite]?.[testName]
1862
1866
  const isLastEfdAttempt = testStatuses.length === efdRetryCount + 1
1863
1867
  if (efdRetryCount > 0 && isLastEfdAttempt && testStatuses.every(status => status === 'fail')) {
1864
1868
  this.activeTestSpan.setTag(TEST_HAS_FAILED_ALL_RETRIES, 'true')
@@ -1,12 +1,15 @@
1
1
  'use strict'
2
2
 
3
+ // No optional chaining or nullish coalescing in this file: Cypress 12 bundles it with webpack 4,
4
+ // whose parser predates both, and its babel-loader skips node_modules.
5
+
3
6
  let rumFlushWaitMillis = 500
4
7
  let rumTestExecutionIdCookieName
5
8
 
6
9
  let isEarlyFlakeDetectionEnabled = false
7
10
  let isKnownTestsEnabled = false
8
11
  let knownTestsForSuite = []
9
- let earlyFlakeDetectionNumRetries = 0
12
+ let earlyFlakeDetectionSchedulingRetryCount = 0
10
13
  let isTestManagementEnabled = false
11
14
  let testManagementAttemptToFixRetries = 0
12
15
  let testManagementTests = {}
@@ -336,7 +339,7 @@ Cypress.mocha.getRunner().runTests = function (suite, fn) {
336
339
  } else if (isModified && isEarlyFlakeDetectionEnabled) {
337
340
  disableFrameworkRetries(test)
338
341
  retryMessage = 'to detect flakes because it is modified'
339
- retriedTests = getRetriedTests(test, earlyFlakeDetectionNumRetries, [
342
+ retriedTests = getRetriedTests(test, earlyFlakeDetectionSchedulingRetryCount, [
340
343
  '_ddIsModified',
341
344
  '_ddIsEfdRetry',
342
345
  isKnownTestsEnabled && isNewTest(test) && '_ddIsNew',
@@ -344,7 +347,7 @@ Cypress.mocha.getRunner().runTests = function (suite, fn) {
344
347
  } else if (isNew && isEarlyFlakeDetectionEnabled) {
345
348
  disableFrameworkRetries(test)
346
349
  retryMessage = 'to detect flakes because it is new'
347
- retriedTests = getRetriedTests(test, earlyFlakeDetectionNumRetries, ['_ddIsNew', '_ddIsEfdRetry'])
350
+ retriedTests = getRetriedTests(test, earlyFlakeDetectionSchedulingRetryCount, ['_ddIsNew', '_ddIsEfdRetry'])
348
351
  }
349
352
 
350
353
  testsWithRetries.push(...retriedTests)
@@ -428,7 +431,7 @@ before(function () {
428
431
  isEarlyFlakeDetectionEnabled = suiteConfig.isEarlyFlakeDetectionEnabled
429
432
  isKnownTestsEnabled = suiteConfig.isKnownTestsEnabled
430
433
  knownTestsForSuite = suiteConfig.knownTestsForSuite
431
- earlyFlakeDetectionNumRetries = suiteConfig.earlyFlakeDetectionNumRetries
434
+ earlyFlakeDetectionSchedulingRetryCount = suiteConfig.earlyFlakeDetectionSchedulingRetryCount
432
435
  isTestManagementEnabled = suiteConfig.isTestManagementEnabled
433
436
  testManagementAttemptToFixRetries = suiteConfig.testManagementAttemptToFixRetries
434
437
  testManagementTests = suiteConfig.testManagementTests
@@ -64,12 +64,18 @@ class ElectronRequestPlugin extends HttpClientPlugin {
64
64
  const responseHead = ctx.res?._responseHead
65
65
  const { statusCode } = responseHead || {}
66
66
 
67
- for (const header in ctx.req._urlLoaderOptions?.headers || {}) {
68
- reqHeaders[header.name] = header.value
67
+ const requestHeaders = ctx.req._urlLoaderOptions?.headers
68
+ if (requestHeaders) {
69
+ for (const header in requestHeaders) {
70
+ reqHeaders[header.name] = header.value
71
+ }
69
72
  }
70
73
 
71
- for (const header in responseHead?.rawHeaders || {}) {
72
- resHeaders[header.name] = header.value
74
+ const responseHeaders = responseHead?.rawHeaders
75
+ if (responseHeaders) {
76
+ for (const header in responseHeaders) {
77
+ resHeaders[header.name] = header.value
78
+ }
73
79
  }
74
80
 
75
81
  ctx.req = { headers: reqHeaders }
@@ -1,8 +1,17 @@
1
1
  'use strict'
2
2
 
3
- const ProducerPlugin = require('../../dd-trace/src/plugins/producer')
3
+ const {
4
+ readDatadogParentId,
5
+ readDatadogSamplingPriority,
6
+ readDatadogTraceId,
7
+ readTraceparent,
8
+ writeDatadogParentId,
9
+ writeDatadogSamplingPriority,
10
+ writeDatadogTraceId,
11
+ } = require('../../dd-trace/src/carrier')
4
12
  const { DsmPathwayCodec, getHeadersSize } = require('../../dd-trace/src/datastreams')
5
13
  const id = require('../../dd-trace/src/id')
14
+ const ProducerPlugin = require('../../dd-trace/src/plugins/producer')
6
15
 
7
16
  class GoogleCloudPubsubProducerPlugin extends ProducerPlugin {
8
17
  static id = 'google-cloud-pubsub'
@@ -21,7 +30,7 @@ class GoogleCloudPubsubProducerPlugin extends ProducerPlugin {
21
30
 
22
31
  handleMessagePublish ({ attributes, pubsub, topicName }) {
23
32
  // Skip if message already has trace context from upstream
24
- if (attributes['x-datadog-trace-id'] || attributes.traceparent) return
33
+ if (readDatadogTraceId(attributes) || readTraceparent(attributes)) return
25
34
 
26
35
  const activeSpan = this.tracer.scope().active()
27
36
  if (!activeSpan) return
@@ -62,7 +71,7 @@ class GoogleCloudPubsubProducerPlugin extends ProducerPlugin {
62
71
  const messages = request.messages || []
63
72
  const topic = request.topic
64
73
  const messageCount = messages.length
65
- const hasTraceContext = messages[0]?.attributes?.['x-datadog-trace-id']
74
+ const hasTraceContext = messages[0]?.attributes && readDatadogTraceId(messages[0].attributes)
66
75
 
67
76
  /**
68
77
  * Batch Publishing Strategy:
@@ -82,12 +91,14 @@ class GoogleCloudPubsubProducerPlugin extends ProducerPlugin {
82
91
  }
83
92
 
84
93
  const firstAttrs = messages[0]?.attributes
85
- const parentData = firstAttrs?.['x-datadog-trace-id'] && firstAttrs['x-datadog-parent-id']
94
+ const traceId = firstAttrs && readDatadogTraceId(firstAttrs)
95
+ const spanId = firstAttrs && readDatadogParentId(firstAttrs)
96
+ const parentData = traceId && spanId
86
97
  ? {
87
- traceId: firstAttrs['x-datadog-trace-id'],
88
- spanId: firstAttrs['x-datadog-parent-id'],
98
+ traceId,
99
+ spanId,
89
100
  traceIdUpper: firstAttrs['_dd.p.tid'],
90
- samplingPriority: firstAttrs['x-datadog-sampling-priority'],
101
+ samplingPriority: readDatadogSamplingPriority(firstAttrs),
91
102
  }
92
103
  : null
93
104
 
@@ -174,33 +185,36 @@ class GoogleCloudPubsubProducerPlugin extends ProducerPlugin {
174
185
  }
175
186
 
176
187
  #extractSpanLink (attrs) {
177
- if (!attrs?.['x-datadog-trace-id'] || !attrs['x-datadog-parent-id']) return null
188
+ if (!attrs) return null
189
+ const traceId = readDatadogTraceId(attrs)
190
+ const spanId = readDatadogParentId(attrs)
191
+ if (!traceId || !spanId) return null
178
192
 
179
193
  // Convert to hex strings
180
- const lowerHex = id(attrs['x-datadog-trace-id']).toString(16)
181
- const spanIdHex = id(attrs['x-datadog-parent-id']).toString(16)
194
+ const lowerHex = id(traceId).toString(16)
195
+ const spanIdHex = id(spanId).toString(16)
182
196
 
183
197
  // Build full 128-bit trace ID
184
198
  const traceIdHex = attrs['_dd.p.tid']
185
199
  ? attrs['_dd.p.tid'] + lowerHex
186
200
  : lowerHex.padStart(32, '0')
187
201
 
202
+ const samplingPriority = readDatadogSamplingPriority(attrs)
188
203
  return {
189
204
  traceId: traceIdHex,
190
205
  spanId: spanIdHex,
191
- samplingPriority: attrs['x-datadog-sampling-priority']
192
- ? Number.parseInt(attrs['x-datadog-sampling-priority'], 10)
206
+ samplingPriority: samplingPriority
207
+ ? Number.parseInt(samplingPriority, 10)
193
208
  : undefined,
194
209
  }
195
210
  }
196
211
 
197
212
  #extractParentContext (data) {
198
- const carrier = {
199
- 'x-datadog-trace-id': data.traceId,
200
- 'x-datadog-parent-id': data.spanId,
201
- }
213
+ const carrier = /** @type {Record<string, unknown>} */ ({})
214
+ writeDatadogTraceId(carrier, data.traceId)
215
+ writeDatadogParentId(carrier, data.spanId)
202
216
  if (data.traceIdUpper) carrier['_dd.p.tid'] = data.traceIdUpper
203
- if (data.samplingPriority) carrier['x-datadog-sampling-priority'] = String(data.samplingPriority)
217
+ if (data.samplingPriority) writeDatadogSamplingPriority(carrier, String(data.samplingPriority))
204
218
 
205
219
  return this.tracer.extract('text_map', carrier)
206
220
  }
@@ -56,7 +56,7 @@ class HttpClientPlugin extends ClientPlugin {
56
56
  'out.host': hostname,
57
57
  },
58
58
  metrics: {
59
- [CLIENT_PORT_KEY]: Number.parseInt(options.port),
59
+ [CLIENT_PORT_KEY]: Number.parseInt(options.port, 10),
60
60
  },
61
61
  }, false)
62
62
 
@@ -55,7 +55,7 @@ class Http2ClientPlugin extends ClientPlugin {
55
55
  'out.host': sessionDetails.host,
56
56
  },
57
57
  metrics: {
58
- [CLIENT_PORT_KEY]: Number.parseInt(sessionDetails.port),
58
+ [CLIENT_PORT_KEY]: Number.parseInt(sessionDetails.port, 10),
59
59
  },
60
60
  }, false)
61
61