dd-trace 5.102.0 → 5.104.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 (201) hide show
  1. package/ext/exporters.js +1 -0
  2. package/index.d.ts +25 -3
  3. package/package.json +15 -13
  4. package/packages/datadog-esbuild/src/utils.js +2 -2
  5. package/packages/datadog-instrumentations/src/ai.js +1 -1
  6. package/packages/datadog-instrumentations/src/aws-sdk.js +2 -2
  7. package/packages/datadog-instrumentations/src/cassandra-driver.js +5 -2
  8. package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +32 -15
  9. package/packages/datadog-instrumentations/src/couchbase.js +69 -220
  10. package/packages/datadog-instrumentations/src/cucumber.js +104 -31
  11. package/packages/datadog-instrumentations/src/elasticsearch.js +4 -4
  12. package/packages/datadog-instrumentations/src/electron/preload.js +42 -0
  13. package/packages/datadog-instrumentations/src/electron.js +240 -0
  14. package/packages/datadog-instrumentations/src/fetch.js +5 -5
  15. package/packages/datadog-instrumentations/src/graphql.js +13 -17
  16. package/packages/datadog-instrumentations/src/grpc/client.js +48 -32
  17. package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +2 -2
  18. package/packages/datadog-instrumentations/src/helpers/hook.js +4 -1
  19. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  20. package/packages/datadog-instrumentations/src/helpers/instrument.js +2 -2
  21. package/packages/datadog-instrumentations/src/helpers/kafka.js +58 -0
  22. package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +3 -2
  23. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +19 -5
  24. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +14 -13
  25. package/packages/datadog-instrumentations/src/http/client.js +2 -2
  26. package/packages/datadog-instrumentations/src/ioredis.js +18 -14
  27. package/packages/datadog-instrumentations/src/jest.js +382 -84
  28. package/packages/datadog-instrumentations/src/kafkajs.js +184 -174
  29. package/packages/datadog-instrumentations/src/mariadb.js +1 -1
  30. package/packages/datadog-instrumentations/src/memcached.js +2 -1
  31. package/packages/datadog-instrumentations/src/mocha/main.js +309 -56
  32. package/packages/datadog-instrumentations/src/mocha/utils.js +48 -8
  33. package/packages/datadog-instrumentations/src/mongodb-core.js +34 -9
  34. package/packages/datadog-instrumentations/src/mongoose.js +10 -12
  35. package/packages/datadog-instrumentations/src/mysql.js +2 -2
  36. package/packages/datadog-instrumentations/src/mysql2.js +1 -1
  37. package/packages/datadog-instrumentations/src/pg.js +25 -11
  38. package/packages/datadog-instrumentations/src/playwright.js +449 -60
  39. package/packages/datadog-instrumentations/src/redis.js +19 -10
  40. package/packages/datadog-instrumentations/src/router.js +4 -2
  41. package/packages/datadog-instrumentations/src/vitest.js +246 -149
  42. package/packages/datadog-plugin-apollo/src/gateway/request.js +1 -21
  43. package/packages/datadog-plugin-aws-sdk/src/base.js +18 -24
  44. package/packages/datadog-plugin-aws-sdk/src/services/cloudwatchlogs.js +1 -1
  45. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +1 -1
  46. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -1
  47. package/packages/datadog-plugin-aws-sdk/src/services/lambda.js +1 -1
  48. package/packages/datadog-plugin-aws-sdk/src/services/redshift.js +1 -1
  49. package/packages/datadog-plugin-aws-sdk/src/services/s3.js +1 -1
  50. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +2 -2
  51. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -1
  52. package/packages/datadog-plugin-aws-sdk/src/services/stepfunctions.js +1 -1
  53. package/packages/datadog-plugin-couchbase/src/index.js +58 -52
  54. package/packages/datadog-plugin-cucumber/src/index.js +1 -0
  55. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +239 -40
  56. package/packages/datadog-plugin-cypress/src/support.js +13 -1
  57. package/packages/datadog-plugin-elasticsearch/src/index.js +28 -8
  58. package/packages/datadog-plugin-electron/src/index.js +17 -0
  59. package/packages/datadog-plugin-electron/src/ipc.js +143 -0
  60. package/packages/datadog-plugin-electron/src/net.js +82 -0
  61. package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +27 -18
  62. package/packages/datadog-plugin-graphql/src/execute.js +6 -28
  63. package/packages/datadog-plugin-graphql/src/resolve.js +30 -35
  64. package/packages/datadog-plugin-graphql/src/tools/signature.js +32 -7
  65. package/packages/datadog-plugin-graphql/src/tools/transforms.js +118 -100
  66. package/packages/datadog-plugin-graphql/src/utils.js +33 -1
  67. package/packages/datadog-plugin-grpc/src/client.js +6 -7
  68. package/packages/datadog-plugin-grpc/src/util.js +57 -22
  69. package/packages/datadog-plugin-http/src/client.js +2 -2
  70. package/packages/datadog-plugin-jest/src/index.js +92 -50
  71. package/packages/datadog-plugin-kafkajs/src/producer.js +32 -0
  72. package/packages/datadog-plugin-mocha/src/index.js +1 -0
  73. package/packages/datadog-plugin-mongodb-core/src/index.js +70 -69
  74. package/packages/datadog-plugin-mysql/src/index.js +1 -1
  75. package/packages/datadog-plugin-openai/src/services.js +2 -1
  76. package/packages/datadog-plugin-pg/src/index.js +3 -3
  77. package/packages/datadog-plugin-playwright/src/index.js +4 -0
  78. package/packages/datadog-plugin-redis/src/index.js +54 -24
  79. package/packages/datadog-plugin-undici/src/index.js +19 -0
  80. package/packages/datadog-plugin-vitest/src/index.js +19 -7
  81. package/packages/datadog-shimmer/src/shimmer.js +35 -0
  82. package/packages/dd-trace/src/aiguard/index.js +3 -1
  83. package/packages/dd-trace/src/aiguard/sdk.js +36 -30
  84. package/packages/dd-trace/src/aiguard/tags.js +20 -11
  85. package/packages/dd-trace/src/appsec/blocking.js +2 -2
  86. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +2 -2
  87. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +1 -1
  88. package/packages/dd-trace/src/appsec/index.js +10 -3
  89. package/packages/dd-trace/src/appsec/reporter.js +19 -5
  90. package/packages/dd-trace/src/azure_metadata.js +17 -6
  91. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +4 -4
  92. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +4 -2
  93. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +6 -4
  94. package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +1 -1
  95. package/packages/dd-trace/src/ci-visibility/requests/request.js +3 -1
  96. package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +5 -3
  97. package/packages/dd-trace/src/config/defaults.js +3 -14
  98. package/packages/dd-trace/src/config/generated-config-types.d.ts +4 -1
  99. package/packages/dd-trace/src/config/helper.js +4 -0
  100. package/packages/dd-trace/src/config/index.js +2 -2
  101. package/packages/dd-trace/src/config/major-overrides.js +98 -0
  102. package/packages/dd-trace/src/config/parsers.js +7 -1
  103. package/packages/dd-trace/src/config/supported-configurations.json +60 -38
  104. package/packages/dd-trace/src/crashtracking/crashtracker.js +15 -3
  105. package/packages/dd-trace/src/datastreams/checkpointer.js +2 -2
  106. package/packages/dd-trace/src/datastreams/context.js +4 -2
  107. package/packages/dd-trace/src/datastreams/manager.js +1 -1
  108. package/packages/dd-trace/src/datastreams/processor.js +2 -2
  109. package/packages/dd-trace/src/debugger/devtools_client/snapshot/collector.js +2 -2
  110. package/packages/dd-trace/src/debugger/devtools_client/source-maps.js +1 -1
  111. package/packages/dd-trace/src/debugger/devtools_client/state.js +2 -1
  112. package/packages/dd-trace/src/debugger/index.js +7 -7
  113. package/packages/dd-trace/src/dogstatsd.js +2 -2
  114. package/packages/dd-trace/src/encode/0.4.js +45 -54
  115. package/packages/dd-trace/src/encode/0.5.js +34 -3
  116. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +26 -19
  117. package/packages/dd-trace/src/encode/agentless-json.js +1 -1
  118. package/packages/dd-trace/src/exporter.js +2 -0
  119. package/packages/dd-trace/src/exporters/agent/index.js +2 -1
  120. package/packages/dd-trace/src/exporters/agentless/index.js +3 -2
  121. package/packages/dd-trace/src/exporters/agentless/writer.js +2 -2
  122. package/packages/dd-trace/src/exporters/common/agents.js +3 -1
  123. package/packages/dd-trace/src/exporters/common/buffering-exporter.js +2 -1
  124. package/packages/dd-trace/src/exporters/common/request.js +4 -2
  125. package/packages/dd-trace/src/exporters/electron/index.js +49 -0
  126. package/packages/dd-trace/src/external-logger/src/index.js +2 -1
  127. package/packages/dd-trace/src/git_metadata.js +10 -8
  128. package/packages/dd-trace/src/id.js +17 -4
  129. package/packages/dd-trace/src/lambda/handler-paths.js +52 -0
  130. package/packages/dd-trace/src/lambda/handler.js +2 -4
  131. package/packages/dd-trace/src/lambda/index.js +62 -14
  132. package/packages/dd-trace/src/lambda/runtime/patch.js +21 -46
  133. package/packages/dd-trace/src/llmobs/index.js +13 -2
  134. package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +45 -15
  135. package/packages/dd-trace/src/llmobs/sdk.js +10 -0
  136. package/packages/dd-trace/src/llmobs/writers/base.js +2 -1
  137. package/packages/dd-trace/src/log/writer.js +3 -1
  138. package/packages/dd-trace/src/noop/span.js +3 -1
  139. package/packages/dd-trace/src/openfeature/writers/base.js +2 -1
  140. package/packages/dd-trace/src/openfeature/writers/exposures.js +51 -20
  141. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +3 -2
  142. package/packages/dd-trace/src/opentracing/propagation/text_map.js +20 -9
  143. package/packages/dd-trace/src/payload-tagging/config/index.js +2 -2
  144. package/packages/dd-trace/src/plugins/apollo.js +3 -1
  145. package/packages/dd-trace/src/plugins/ci_plugin.js +52 -17
  146. package/packages/dd-trace/src/plugins/database.js +54 -12
  147. package/packages/dd-trace/src/plugins/index.js +1 -0
  148. package/packages/dd-trace/src/plugins/log_plugin.js +3 -1
  149. package/packages/dd-trace/src/plugins/plugin.js +2 -4
  150. package/packages/dd-trace/src/plugins/tracing.js +5 -3
  151. package/packages/dd-trace/src/plugins/util/ci.js +8 -8
  152. package/packages/dd-trace/src/plugins/util/git-cache.js +20 -18
  153. package/packages/dd-trace/src/plugins/util/git.js +3 -1
  154. package/packages/dd-trace/src/plugins/util/stacktrace.js +2 -2
  155. package/packages/dd-trace/src/plugins/util/test.js +119 -5
  156. package/packages/dd-trace/src/plugins/util/user-provided-git.js +17 -15
  157. package/packages/dd-trace/src/plugins/util/web.js +11 -0
  158. package/packages/dd-trace/src/priority_sampler.js +1 -1
  159. package/packages/dd-trace/src/profiling/profiler.js +1 -1
  160. package/packages/dd-trace/src/profiling/profilers/wall.js +1 -1
  161. package/packages/dd-trace/src/profiling/ssi-heuristics.js +1 -1
  162. package/packages/dd-trace/src/rate_limiter.js +1 -1
  163. package/packages/dd-trace/src/remote_config/scheduler.js +1 -1
  164. package/packages/dd-trace/src/ritm.js +2 -1
  165. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +5 -8
  166. package/packages/dd-trace/src/scope.js +7 -5
  167. package/packages/dd-trace/src/serverless.js +5 -2
  168. package/packages/dd-trace/src/service-naming/extra-services.js +14 -0
  169. package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +20 -0
  170. package/packages/dd-trace/src/service-naming/schemas/v0/web.js +4 -0
  171. package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +20 -0
  172. package/packages/dd-trace/src/service-naming/schemas/v1/web.js +4 -0
  173. package/packages/dd-trace/src/span_stats.js +1 -1
  174. package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
  175. package/packages/dd-trace/src/telemetry/endpoints.js +1 -1
  176. package/packages/dd-trace/src/telemetry/telemetry.js +2 -2
  177. package/packages/dd-trace/src/lambda/runtime/ritm.js +0 -133
  178. package/vendor/dist/opentracing/LICENSE +0 -201
  179. package/vendor/dist/opentracing/binary_carrier.d.ts +0 -11
  180. package/vendor/dist/opentracing/constants.d.ts +0 -61
  181. package/vendor/dist/opentracing/examples/demo/demo.d.ts +0 -2
  182. package/vendor/dist/opentracing/ext/tags.d.ts +0 -90
  183. package/vendor/dist/opentracing/functions.d.ts +0 -20
  184. package/vendor/dist/opentracing/global_tracer.d.ts +0 -14
  185. package/vendor/dist/opentracing/index.d.ts +0 -12
  186. package/vendor/dist/opentracing/index.js +0 -1
  187. package/vendor/dist/opentracing/mock_tracer/index.d.ts +0 -5
  188. package/vendor/dist/opentracing/mock_tracer/mock_context.d.ts +0 -13
  189. package/vendor/dist/opentracing/mock_tracer/mock_report.d.ts +0 -16
  190. package/vendor/dist/opentracing/mock_tracer/mock_span.d.ts +0 -50
  191. package/vendor/dist/opentracing/mock_tracer/mock_tracer.d.ts +0 -26
  192. package/vendor/dist/opentracing/noop.d.ts +0 -8
  193. package/vendor/dist/opentracing/reference.d.ts +0 -33
  194. package/vendor/dist/opentracing/span.d.ts +0 -147
  195. package/vendor/dist/opentracing/span_context.d.ts +0 -26
  196. package/vendor/dist/opentracing/test/api_compatibility.d.ts +0 -16
  197. package/vendor/dist/opentracing/test/mocktracer_implemenation.d.ts +0 -3
  198. package/vendor/dist/opentracing/test/noop_implementation.d.ts +0 -4
  199. package/vendor/dist/opentracing/test/opentracing_api.d.ts +0 -3
  200. package/vendor/dist/opentracing/test/unittest.d.ts +0 -2
  201. package/vendor/dist/opentracing/tracer.d.ts +0 -127
@@ -15,10 +15,12 @@ const {
15
15
  CUCUMBER_WORKER_TRACE_PAYLOAD_CODE,
16
16
  getIsFaultyEarlyFlakeDetection,
17
17
  getEfdRetryCount,
18
+ getMaxEfdRetryCount,
18
19
  recordAttemptToFixExecution,
19
20
  collectAttemptToFixExecutionsFromTraces,
20
21
  logAttemptToFixTestExecution,
21
22
  logTestOptimizationSummary,
23
+ getTestOptimizationRequestResults,
22
24
  } = require('../../dd-trace/src/plugins/util/test')
23
25
  const satisfies = require('../../../vendor/dist/semifies')
24
26
  const { addHook, channel } = require('./helpers/instrument')
@@ -51,6 +53,8 @@ const itrSkippedSuitesCh = channel('ci:cucumber:itr:skipped-suites')
51
53
 
52
54
  const getCodeCoverageCh = channel('ci:nyc:get-coverage')
53
55
 
56
+ const DD_EFD_RETRY_COUNT_MESSAGE = '_ddEfdRetryCount'
57
+
54
58
  const isMarkedAsUnskippable = (pickle) => {
55
59
  return pickle.tags.some(tag => tag.name === '@datadog:unskippable')
56
60
  }
@@ -67,7 +71,7 @@ const atrStatusesByScenarioKey = new Map()
67
71
  const numRetriesByPickleId = new Map()
68
72
  const efdRetryCountByPickleId = new Map()
69
73
  const efdSlowAbortedPickleIds = new Set()
70
- const testCaseStartedTimesById = new Map()
74
+ const finishedParallelSuites = new Set()
71
75
  const numAttemptToCtx = new Map()
72
76
  const newTestsByTestFullname = new Map()
73
77
  const attemptToFixTestsByTestFullname = new Map()
@@ -117,6 +121,68 @@ function getSuiteStatusFromTestStatuses (testStatuses) {
117
121
  return 'pass'
118
122
  }
119
123
 
124
+ function getConfiguredEfdRetryCount () {
125
+ const maxSlowTestRetryCount = getMaxEfdRetryCount(earlyFlakeDetectionSlowTestRetries)
126
+ return maxSlowTestRetryCount || earlyFlakeDetectionNumRetries
127
+ }
128
+
129
+ function publishWorkerEfdRetryCount (pickle, retryCount) {
130
+ if (typeof process.send !== 'function') return
131
+
132
+ try {
133
+ process.send({
134
+ [DD_EFD_RETRY_COUNT_MESSAGE]: {
135
+ pickleId: pickle.id,
136
+ retryCount,
137
+ testFileAbsolutePath: pickle.uri,
138
+ testName: pickle.name,
139
+ },
140
+ })
141
+ } catch {
142
+ // ignore IPC errors
143
+ }
144
+ }
145
+
146
+ function finishParallelSuiteIfDone (testFileAbsolutePath) {
147
+ const finished = pickleResultByFile[testFileAbsolutePath]
148
+ const expectedPickles = pickleByFile[testFileAbsolutePath]
149
+
150
+ if (!finished || !expectedPickles || finished.length !== expectedPickles.length) return
151
+ if (finishedParallelSuites.has(testFileAbsolutePath)) return
152
+
153
+ finishedParallelSuites.add(testFileAbsolutePath)
154
+ testSuiteFinishCh.publish({
155
+ status: getSuiteStatusFromTestStatuses(finished),
156
+ testSuitePath: getTestSuitePath(testFileAbsolutePath, process.cwd()),
157
+ })
158
+ }
159
+
160
+ function maybeRecordFinalParallelEfdStatus ({ pickleId, testFileAbsolutePath, testFullname }) {
161
+ const efdRetryCount = efdRetryCountByPickleId.get(pickleId)
162
+ const testStatuses = newTestsByTestFullname.get(testFullname)
163
+ const finished = pickleResultByFile[testFileAbsolutePath]
164
+
165
+ if (efdRetryCount === undefined || !testStatuses || !finished) return
166
+ if (testStatuses.length !== efdRetryCount + 1) return
167
+
168
+ finished.push(getTestStatusFromRetries(testStatuses))
169
+ newTestsByTestFullname.delete(testFullname)
170
+ finishParallelSuiteIfDone(testFileAbsolutePath)
171
+ }
172
+
173
+ function handleEfdRetryCountMessage (message) {
174
+ const { pickleId, retryCount, testFileAbsolutePath, testName } = message
175
+
176
+ if (!pickleId || typeof retryCount !== 'number' || !testFileAbsolutePath || !testName) return
177
+
178
+ efdRetryCountByPickleId.set(pickleId, retryCount)
179
+ maybeRecordFinalParallelEfdStatus({
180
+ pickleId,
181
+ testFileAbsolutePath,
182
+ testFullname: `${testFileAbsolutePath}:${testName}`,
183
+ })
184
+ }
185
+
120
186
  function getStatusFromResult (result) {
121
187
  if (result.status === 1) {
122
188
  return { status: 'pass' }
@@ -639,21 +705,34 @@ function getWrappedStart (start, frameworkVersion, isParallel = false, isCoordin
639
705
  testManagementAttemptToFixRetries = configurationResponse.libraryConfig?.testManagementAttemptToFixRetries
640
706
  isImpactedTestsEnabled = configurationResponse.libraryConfig?.isImpactedTestsEnabled
641
707
 
708
+ const {
709
+ knownTestsResponse,
710
+ testManagementTestsResponse,
711
+ skippableSuitesResponse,
712
+ } = await getTestOptimizationRequestResults({
713
+ isKnownTestsEnabled,
714
+ isTestManagementTestsEnabled,
715
+ isSuitesSkippingEnabled,
716
+ getKnownTests: () => getChannelPromise(knownTestsCh),
717
+ getTestManagementTests: () => getChannelPromise(testManagementTestsCh),
718
+ getSkippableSuites: () => getChannelPromise(skippableSuitesCh),
719
+ })
720
+
642
721
  if (isKnownTestsEnabled) {
643
- const knownTestsResponse = await getChannelPromise(knownTestsCh)
644
- if (knownTestsResponse.err) {
722
+ const currentKnownTestsResponse = knownTestsResponse || await getChannelPromise(knownTestsCh)
723
+ if (currentKnownTestsResponse.err) {
645
724
  isEarlyFlakeDetectionEnabled = false
646
725
  isKnownTestsEnabled = false
647
726
  } else {
648
- knownTests = knownTestsResponse.knownTests
727
+ knownTests = currentKnownTestsResponse.knownTests
649
728
  }
650
729
  }
651
730
 
652
731
  if (isSuitesSkippingEnabled) {
653
- const skippableResponse = await getChannelPromise(skippableSuitesCh)
732
+ const skippableResponse = skippableSuitesResponse || await getChannelPromise(skippableSuitesCh)
654
733
 
655
734
  errorSkippableRequest = skippableResponse.err
656
- skippableSuites = skippableResponse.skippableSuites
735
+ skippableSuites = skippableResponse.skippableSuites ?? []
657
736
 
658
737
  if (!errorSkippableRequest) {
659
738
  const filteredPickles = isCoordinator
@@ -694,11 +773,12 @@ function getWrappedStart (start, frameworkVersion, isParallel = false, isCoordin
694
773
  }
695
774
 
696
775
  if (isTestManagementTestsEnabled) {
697
- const testManagementTestsResponse = await getChannelPromise(testManagementTestsCh)
698
- if (testManagementTestsResponse.err) {
776
+ const currentTestManagementTestsResponse =
777
+ testManagementTestsResponse || await getChannelPromise(testManagementTestsCh)
778
+ if (currentTestManagementTestsResponse.err) {
699
779
  isTestManagementTestsEnabled = false
700
780
  } else {
701
- testManagementTests = testManagementTestsResponse.testManagementTests
781
+ testManagementTests = currentTestManagementTestsResponse.testManagementTests
702
782
  }
703
783
  }
704
784
 
@@ -720,7 +800,7 @@ function getWrappedStart (start, frameworkVersion, isParallel = false, isCoordin
720
800
  attemptToFixTestsByTestFullname.clear()
721
801
  efdRetryCountByPickleId.clear()
722
802
  efdSlowAbortedPickleIds.clear()
723
- testCaseStartedTimesById.clear()
803
+ finishedParallelSuites.clear()
724
804
  newTestsByTestFullname.clear()
725
805
  sessionStartCh.publish({ command, frameworkVersion })
726
806
 
@@ -875,6 +955,9 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
875
955
  efdSlowAbortedPickleIds.add(pickle.id)
876
956
  }
877
957
  }
958
+ if (isWorker) {
959
+ publishWorkerEfdRetryCount(pickle, efdRetryCount)
960
+ }
878
961
  for (let retryIndex = 0; retryIndex < efdRetryCount; retryIndex++) {
879
962
  numRetriesByPickleId.set(pickle.id, retryIndex + 1)
880
963
  // eslint-disable-next-line no-await-in-loop
@@ -973,6 +1056,10 @@ function getWrappedParseWorkerMessage (parseWorkerMessageFunction, isNewVersion)
973
1056
  return
974
1057
  }
975
1058
  }
1059
+ if (message[DD_EFD_RETRY_COUNT_MESSAGE]) {
1060
+ handleEfdRetryCountMessage(message[DD_EFD_RETRY_COUNT_MESSAGE])
1061
+ return
1062
+ }
976
1063
 
977
1064
  const envelope = isNewVersion ? message.envelope : message.jsonEnvelope
978
1065
 
@@ -989,12 +1076,13 @@ function getWrappedParseWorkerMessage (parseWorkerMessageFunction, isNewVersion)
989
1076
  return parseWorkerMessageFunction.apply(this, arguments)
990
1077
  }
991
1078
  }
1079
+ if (parsed[DD_EFD_RETRY_COUNT_MESSAGE]) {
1080
+ handleEfdRetryCountMessage(parsed[DD_EFD_RETRY_COUNT_MESSAGE])
1081
+ return
1082
+ }
992
1083
  let pickle
993
1084
 
994
1085
  if (parsed.testCaseStarted) {
995
- if (parsed.testCaseStarted.id) {
996
- testCaseStartedTimesById.set(parsed.testCaseStarted.id, performance.now())
997
- }
998
1086
  if (isNewVersion) {
999
1087
  pickle = this.inProgress[worker.id].pickle
1000
1088
  } else {
@@ -1016,10 +1104,6 @@ function getWrappedParseWorkerMessage (parseWorkerMessageFunction, isNewVersion)
1016
1104
 
1017
1105
  // after calling `parseWorkerMessageFunction`, the test status can already be read
1018
1106
  if (parsed.testCaseFinished) {
1019
- const testCaseStartedId = parsed.testCaseFinished.testCaseStartedId
1020
- const testCaseStartedAt = testCaseStartedTimesById.get(testCaseStartedId)
1021
- testCaseStartedTimesById.delete(testCaseStartedId)
1022
-
1023
1107
  let worstTestStepResult
1024
1108
  if (isNewVersion && eventDataCollector) {
1025
1109
  pickle = this.inProgress[worker.id].pickle
@@ -1052,21 +1136,15 @@ function getWrappedParseWorkerMessage (parseWorkerMessageFunction, isNewVersion)
1052
1136
  }
1053
1137
  let efdRetryCount = efdRetryCountByPickleId.get(pickle.id)
1054
1138
  if (efdRetryCount === undefined) {
1055
- const firstExecutionDurationMs = testCaseStartedAt === undefined ? 0 : performance.now() - testCaseStartedAt
1056
1139
  efdRetryCount = status === 'skip'
1057
1140
  ? 0
1058
- : getEfdRetryCount(firstExecutionDurationMs, earlyFlakeDetectionSlowTestRetries)
1141
+ : getConfiguredEfdRetryCount()
1059
1142
  efdRetryCountByPickleId.set(pickle.id, efdRetryCount)
1060
1143
  if (efdRetryCount === 0 && status !== 'skip') {
1061
1144
  efdSlowAbortedPickleIds.add(pickle.id)
1062
1145
  }
1063
1146
  }
1064
- // We have finished all retries
1065
- if (testStatuses.length === efdRetryCount + 1) {
1066
- const newTestFinalStatus = getTestStatusFromRetries(testStatuses)
1067
- // we only push to `finished` if the retries have finished
1068
- finished.push(newTestFinalStatus)
1069
- }
1147
+ maybeRecordFinalParallelEfdStatus({ pickleId: pickle.id, testFileAbsolutePath, testFullname })
1070
1148
  } else if (
1071
1149
  isTestManagementTestsEnabled &&
1072
1150
  getTestProperties(getTestSuitePath(testFileAbsolutePath, process.cwd()), pickle.name).attemptToFix
@@ -1090,12 +1168,7 @@ function getWrappedParseWorkerMessage (parseWorkerMessageFunction, isNewVersion)
1090
1168
  finished.push(status)
1091
1169
  }
1092
1170
 
1093
- if (finished.length === pickleByFile[testFileAbsolutePath].length) {
1094
- testSuiteFinishCh.publish({
1095
- status: getSuiteStatusFromTestStatuses(finished),
1096
- testSuitePath: getTestSuitePath(testFileAbsolutePath, process.cwd()),
1097
- })
1098
- }
1171
+ finishParallelSuiteIfDone(testFileAbsolutePath)
1099
1172
  }
1100
1173
 
1101
1174
  return parseWorkerResponse
@@ -51,9 +51,9 @@ function createWrapSelect () {
51
51
  const connectCh = channel('apm:elasticsearch:query:connect')
52
52
  return function wrapRequest (request) {
53
53
  return function (...args) {
54
- if (args.length === 1) {
55
- const cb = args[0]
56
- args[0] = shimmer.wrapFunction(cb, cb => function (err, connection) {
54
+ const cb = args[0]
55
+ if (args.length === 1 && typeof cb === 'function') {
56
+ args[0] = shimmer.wrapCallback(cb, cb => function (err, connection) {
57
57
  if (connectCh.hasSubscribers && connection && connection.host) {
58
58
  connectCh.publish({ hostname: connection.host.host, port: connection.host.port })
59
59
  }
@@ -85,7 +85,7 @@ function createWrapRequest (name) {
85
85
  cb = arguments[lastIndex]
86
86
 
87
87
  if (typeof cb === 'function') {
88
- arguments[lastIndex] = shimmer.wrapFunction(cb, cb => function (error) {
88
+ arguments[lastIndex] = shimmer.wrapCallback(cb, cb => function (error) {
89
89
  if (error) {
90
90
  ctx.error = error
91
91
  errorCh.publish(ctx)
@@ -0,0 +1,42 @@
1
+ 'use strict'
2
+
3
+ // eslint-disable-next-line n/no-missing-require
4
+ const { contextBridge, ipcRenderer } = require('electron')
5
+
6
+ const BRIDGE_CHANNEL = 'datadog:bridge-send'
7
+ const CONFIG_CHANNEL = 'datadog:bridge-config'
8
+
9
+ // Privacy levels matching @datadog/browser-core DefaultPrivacyLevel
10
+ const MASK = 'mask'
11
+
12
+ const config = ipcRenderer.sendSync(CONFIG_CHANNEL)
13
+
14
+ const defaultPrivacyLevel = config?.defaultPrivacyLevel ?? MASK
15
+ const configuredHosts = config?.allowedWebViewHosts ?? []
16
+ // eslint-disable-next-line no-undef
17
+ const allowedHosts = [...new Set([location.hostname, ...configuredHosts])]
18
+
19
+ const bridge = {
20
+ getCapabilities () {
21
+ return '[]'
22
+ },
23
+ getPrivacyLevel () {
24
+ return defaultPrivacyLevel
25
+ },
26
+ getAllowedWebViewHosts () {
27
+ return JSON.stringify(allowedHosts)
28
+ },
29
+ send (msg) {
30
+ ipcRenderer.send(BRIDGE_CHANNEL, msg)
31
+ },
32
+ }
33
+
34
+ // Support both contextIsolation enabled (default) and disabled
35
+
36
+ window.DatadogEventBridge = bridge
37
+
38
+ try {
39
+ contextBridge.exposeInMainWorld('DatadogEventBridge', bridge)
40
+ } catch {
41
+ // exposeInMainWorld throws when contextIsolation is disabled
42
+ }
@@ -0,0 +1,240 @@
1
+ 'use strict'
2
+
3
+ const { join } = require('path')
4
+ const { wrap } = require('../../datadog-shimmer')
5
+ const { addHook, channel, tracingChannel } = require('./helpers/instrument')
6
+
7
+ const requestCh = tracingChannel('apm:electron:net:request')
8
+ const mainReceiveCh = tracingChannel('apm:electron:ipc:main:receive')
9
+ const mainHandleCh = tracingChannel('apm:electron:ipc:main:handle')
10
+ const mainSendCh = tracingChannel('apm:electron:ipc:main:send')
11
+ const rendererPatchedCh = channel('apm:electron:ipc:renderer:patched')
12
+ const rendererReceiveCh = tracingChannel('apm:electron:ipc:renderer:receive')
13
+ const rendererSendCh = tracingChannel('apm:electron:ipc:renderer:send')
14
+
15
+ const listeners = {}
16
+ const handlers = {}
17
+
18
+ function createWrapRequest (ch) {
19
+ return function wrapRequest (request) {
20
+ return function (...args) {
21
+ if (!ch.start.hasSubscribers) return request.apply(this, arguments)
22
+
23
+ const ctx = { args }
24
+
25
+ return ch.start.runStores(ctx, () => {
26
+ try {
27
+ const req = request.apply(this, ctx.args)
28
+ const emit = req.emit
29
+
30
+ ctx.req = req
31
+
32
+ req.emit = function (eventName, arg) {
33
+ /* eslint-disable no-fallthrough */
34
+ switch (eventName) {
35
+ case 'response':
36
+ ctx.res = arg
37
+ ctx.res.on('error', error => {
38
+ ctx.error = error
39
+ ch.error.publish(ctx)
40
+ ch.asyncStart.publish(ctx)
41
+ })
42
+ ctx.res.on('end', () => ch.asyncStart.publish(ctx))
43
+ break
44
+ case 'error':
45
+ ctx.error = arg
46
+ ch.error.publish(ctx)
47
+ case 'abort':
48
+ ch.asyncStart.publish(ctx)
49
+ }
50
+
51
+ return emit.apply(this, arguments)
52
+ }
53
+
54
+ return req
55
+ } catch (e) {
56
+ ctx.error = e
57
+ ch.error.publish(ctx)
58
+ throw e
59
+ } finally {
60
+ ch.end.publish(ctx)
61
+ }
62
+ })
63
+ }
64
+ }
65
+ }
66
+
67
+ function createWrapAddListener (ch, mappings) {
68
+ return function wrapAddListener (addListener) {
69
+ return function (channel, listener) {
70
+ const wrappedListener = (event, ...args) => {
71
+ const ctx = { args, channel, event }
72
+
73
+ return ch.tracePromise(() => listener.call(this, event, ...args), ctx)
74
+ }
75
+
76
+ const mapping = mappings[channel] || new WeakMap()
77
+ const wrapper = mapping.get(listener) || wrappedListener
78
+
79
+ mapping.set(listener, wrapper)
80
+
81
+ return addListener.call(this, channel, wrappedListener)
82
+ }
83
+ }
84
+ }
85
+
86
+ function createWrapRemoveListener (mappings) {
87
+ return function wrapRemoveListener (removeListener) {
88
+ return function (channel, listener) {
89
+ const mapping = mappings[channel]
90
+
91
+ if (mapping) {
92
+ const wrapper = mapping.get(listener)
93
+
94
+ if (wrapper) {
95
+ return removeListener.call(this, channel, wrapper)
96
+ }
97
+ }
98
+
99
+ return removeListener.call(this, channel, listener)
100
+ }
101
+ }
102
+ }
103
+
104
+ function createWrapRemoveAllListeners (mappings) {
105
+ return function wrapRemoveAllListeners (removeAllListeners) {
106
+ return function (channel) {
107
+ if (channel) {
108
+ delete mappings[channel]
109
+ } else {
110
+ for (const key of Object.keys(mappings)) delete mappings[key]
111
+ }
112
+
113
+ return removeAllListeners.call(this, channel)
114
+ }
115
+ }
116
+ }
117
+
118
+ function createWrapSend (ch, promise = false) {
119
+ const trace = (promise ? ch.tracePromise : ch.traceSync).bind(ch)
120
+
121
+ return function wrapSend (send) {
122
+ return function (channel, ...args) {
123
+ const ctx = { args, channel, self: this }
124
+
125
+ return trace(() => send.call(this, channel, ...args), ctx)
126
+ }
127
+ }
128
+ }
129
+
130
+ function wrapSendToFrame (send) {
131
+ return function (frameId, channel, ...args) {
132
+ const ctx = { args, channel, frameId, self: this }
133
+
134
+ return mainSendCh.traceSync(() => send.call(this, frameId, channel, ...args), ctx)
135
+ }
136
+ }
137
+
138
+ function wrapBrowserWindow (electron) {
139
+ const moduleExports = {}
140
+
141
+ class DatadogBrowserWindow extends electron.BrowserWindow {
142
+ constructor (options = {}) {
143
+ const win = super(options)
144
+
145
+ win.webContents.session.registerPreloadScript({
146
+ type: 'frame', // TODO: service-worker
147
+ filePath: join(__dirname, 'electron', 'preload.js'),
148
+ })
149
+
150
+ // BrowserWindow doesn't support subclassing because it's all native code
151
+ // so we return an instance of it instead of the subclass.
152
+ return win
153
+ }
154
+ }
155
+
156
+ Object.defineProperty(moduleExports, 'BrowserWindow', {
157
+ enumerable: true,
158
+ get: () => DatadogBrowserWindow,
159
+ configurable: false,
160
+ })
161
+
162
+ for (const key of Reflect.ownKeys(electron)) {
163
+ const descriptor = Reflect.getOwnPropertyDescriptor(electron, key)
164
+
165
+ if (key === 'BrowserWindow') continue
166
+
167
+ Object.defineProperty(moduleExports, key, descriptor)
168
+ }
169
+
170
+ return moduleExports
171
+ }
172
+
173
+ function wrapWebContents (proto) {
174
+ const descriptor = Object.getOwnPropertyDescriptor(proto, 'webContents')
175
+ const wrapped = new WeakSet()
176
+ const wrapSend = createWrapSend(mainSendCh)
177
+
178
+ Object.defineProperty(proto, 'webContents', {
179
+ get () {
180
+ const webContents = descriptor.get.apply(this)
181
+
182
+ if (!wrapped.has(webContents)) {
183
+ // wrap(webContents, 'postMessage', wrapSend)
184
+ wrap(webContents, 'send', wrapSend)
185
+ wrap(webContents, 'sendToFrame', wrapSendToFrame)
186
+
187
+ wrapped.add(webContents)
188
+ }
189
+
190
+ return webContents
191
+ },
192
+ })
193
+ }
194
+
195
+ addHook({ name: 'electron', versions: ['>=37.0.0'] }, electron => {
196
+ // Electron exports a string in Node and an object in Electron.
197
+ if (typeof electron === 'string') return electron
198
+
199
+ const { BrowserWindow, ipcMain, ipcRenderer, net } = electron
200
+
201
+ if (net) {
202
+ // This also covers `fetch` as it uses `request` under the hood.
203
+ wrap(net, 'request', createWrapRequest(requestCh))
204
+ }
205
+
206
+ if (ipcRenderer) {
207
+ wrap(ipcRenderer, 'invoke', createWrapSend(rendererSendCh, true))
208
+ // wrap(ipcRenderer, 'postMessage', createWrapSend(rendererSendCh))
209
+ wrap(ipcRenderer, 'send', createWrapSend(rendererSendCh))
210
+ wrap(ipcRenderer, 'sendSync', createWrapSend(rendererSendCh))
211
+ wrap(ipcRenderer, 'sendToHost', createWrapSend(rendererSendCh))
212
+
213
+ wrap(ipcRenderer, 'addListener', createWrapAddListener(rendererReceiveCh, listeners))
214
+ wrap(ipcRenderer, 'off', createWrapRemoveListener(listeners))
215
+ wrap(ipcRenderer, 'on', createWrapAddListener(rendererReceiveCh, listeners))
216
+ wrap(ipcRenderer, 'once', createWrapAddListener(rendererReceiveCh, listeners))
217
+ wrap(ipcRenderer, 'removeListener', createWrapRemoveListener(listeners))
218
+ wrap(ipcRenderer, 'removeAllListeners', createWrapRemoveAllListeners(listeners))
219
+
220
+ ipcRenderer.send('datadog:apm:renderer:patched')
221
+ } else {
222
+ wrap(ipcMain, 'addListener', createWrapAddListener(mainReceiveCh, listeners))
223
+ wrap(ipcMain, 'handle', createWrapAddListener(mainHandleCh, handlers))
224
+ wrap(ipcMain, 'handleOnce', createWrapAddListener(mainHandleCh, handlers))
225
+ wrap(ipcMain, 'off', createWrapRemoveListener(listeners))
226
+ wrap(ipcMain, 'on', createWrapAddListener(mainReceiveCh, listeners))
227
+ wrap(ipcMain, 'once', createWrapAddListener(mainReceiveCh, listeners))
228
+ wrap(ipcMain, 'removeAllListeners', createWrapRemoveAllListeners(listeners))
229
+ wrap(ipcMain, 'removeHandler', createWrapRemoveAllListeners(handlers))
230
+ wrap(ipcMain, 'removeListener', createWrapRemoveListener(listeners))
231
+
232
+ ipcMain.once('datadog:apm:renderer:patched', event => rendererPatchedCh.publish(event))
233
+
234
+ wrapWebContents(BrowserWindow.prototype)
235
+
236
+ electron = wrapBrowserWindow(electron)
237
+ }
238
+
239
+ return electron
240
+ })
@@ -5,10 +5,10 @@ const { IS_SERVERLESS } = require('../../dd-trace/src/serverless')
5
5
  if (globalThis.fetch) {
6
6
  const globalFetch = globalThis.fetch
7
7
 
8
- let fetch = (input, init) => {
8
+ let wrappedFetch = (input, init) => {
9
9
  wrapRealFetch()
10
10
 
11
- return fetch(input, init)
11
+ return wrappedFetch(input, init)
12
12
  }
13
13
 
14
14
  function wrapRealFetch () {
@@ -20,14 +20,14 @@ if (globalThis.fetch) {
20
20
  channel('dd-trace:instrumentation:load').publish({ name: 'global:fetch' })
21
21
  })
22
22
 
23
- fetch = wrapFetch(globalFetch)
23
+ wrappedFetch = wrapFetch(globalFetch)
24
24
  }
25
25
 
26
26
  if (!IS_SERVERLESS) {
27
27
  wrapRealFetch()
28
28
  }
29
29
 
30
- globalThis.fetch = function value (input, init) {
31
- return fetch(input, init)
30
+ globalThis.fetch = function fetch (input, init) {
31
+ return wrappedFetch(input, init)
32
32
  }
33
33
  }
@@ -169,7 +169,7 @@ function wrapExecute (execute) {
169
169
  const ctx = {
170
170
  operation,
171
171
  args,
172
- docSource: documentSources.get(document),
172
+ docSource: source,
173
173
  source,
174
174
  fields: Object.create(null),
175
175
  abortController: new AbortController(),
@@ -257,13 +257,17 @@ function callInAsyncScope (fn, thisArg, args, abortController, cb) {
257
257
  }
258
258
 
259
259
  function pathToArray (path) {
260
- const flattened = []
261
- let curr = path
262
- while (curr) {
263
- flattened.push(curr.key)
264
- curr = curr.prev
260
+ let length = 0
261
+ for (let curr = path; curr; curr = curr.prev) {
262
+ length += 1
265
263
  }
266
- return flattened.reverse()
264
+
265
+ const flattened = new Array(length)
266
+ let index = length
267
+ for (let curr = path; curr; curr = curr.prev) {
268
+ flattened[--index] = curr.key
269
+ }
270
+ return flattened
267
271
  }
268
272
 
269
273
  function assertField (rootCtx, info, args) {
@@ -295,9 +299,7 @@ function wrapFields (type) {
295
299
 
296
300
  patchedTypes.add(type)
297
301
 
298
- for (const key of Object.keys(type._fields)) {
299
- const field = type._fields[key]
300
-
302
+ for (const field of Object.values(type._fields)) {
301
303
  wrapFieldResolve(field)
302
304
  wrapFieldType(field)
303
305
  }
@@ -321,8 +323,7 @@ function wrapFieldType (field) {
321
323
  }
322
324
 
323
325
  function finishResolvers ({ fields }) {
324
- for (const key of Object.keys(fields).reverse()) {
325
- const field = fields[key]
326
+ for (const field of Object.values(fields)) {
326
327
  field.ctx.finishTime = field.finishTime
327
328
  field.ctx.field = field
328
329
  if (field.error) {
@@ -342,11 +343,6 @@ addHook({ name: '@graphql-tools/executor', versions: ['>=0.0.14'] }, executor =>
342
343
  return executor
343
344
  })
344
345
 
345
- addHook({ name: '@graphql-tools/executor', file: 'cjs/execution/execute.js', versions: ['>=0.0.14'] }, execute => {
346
- shimmer.wrap(execute, 'execute', wrapExecute(execute))
347
- return execute
348
- })
349
-
350
346
  addHook({ name: 'graphql', file: 'execution/execute.js', versions: ['>=0.10'] }, execute => {
351
347
  shimmer.wrap(execute, 'execute', wrapExecute(execute))
352
348
  return execute