dd-trace 5.109.0 → 5.111.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 (208) hide show
  1. package/README.md +13 -8
  2. package/ci/init.js +25 -5
  3. package/ci/vitest-no-worker-init-setup.mjs +430 -0
  4. package/ext/tags.js +2 -0
  5. package/index.d.ts +79 -24
  6. package/initialize.mjs +5 -6
  7. package/loader-hook.mjs +154 -47
  8. package/package.json +20 -16
  9. package/packages/datadog-esbuild/index.js +26 -0
  10. package/packages/datadog-esbuild/src/utils.js +46 -3
  11. package/packages/datadog-instrumentations/src/connect.js +4 -3
  12. package/packages/datadog-instrumentations/src/fastify.js +4 -12
  13. package/packages/datadog-instrumentations/src/fs.js +8 -6
  14. package/packages/datadog-instrumentations/src/graphql.js +26 -484
  15. package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +81 -8
  16. package/packages/datadog-instrumentations/src/helpers/instrument.js +27 -2
  17. package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +65 -0
  18. package/packages/datadog-instrumentations/src/helpers/register.js +6 -4
  19. package/packages/datadog-instrumentations/src/helpers/require-optional-peer.js +17 -0
  20. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql.js +201 -0
  21. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
  22. package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +18 -2
  23. package/packages/datadog-instrumentations/src/koa.js +3 -2
  24. package/packages/datadog-instrumentations/src/mariadb.js +4 -2
  25. package/packages/datadog-instrumentations/src/mocha/main.js +3 -3
  26. package/packages/datadog-instrumentations/src/mongoose.js +14 -2
  27. package/packages/datadog-instrumentations/src/next.js +10 -10
  28. package/packages/datadog-instrumentations/src/otel-sdk-trace.js +7 -6
  29. package/packages/datadog-instrumentations/src/playwright.js +1 -1
  30. package/packages/datadog-instrumentations/src/restify.js +4 -3
  31. package/packages/datadog-instrumentations/src/router.js +18 -5
  32. package/packages/datadog-instrumentations/src/selenium.js +1 -2
  33. package/packages/datadog-instrumentations/src/tedious.js +28 -0
  34. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +1293 -0
  35. package/packages/datadog-instrumentations/src/vitest-main.js +1567 -0
  36. package/packages/datadog-instrumentations/src/vitest-util.js +249 -0
  37. package/packages/datadog-instrumentations/src/vitest-worker.js +753 -0
  38. package/packages/datadog-instrumentations/src/vitest.js +11 -1598
  39. package/packages/datadog-plugin-avsc/src/schema_iterator.js +2 -2
  40. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/client.js +2 -2
  41. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/context.js +18 -10
  42. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +47 -11
  43. package/packages/datadog-plugin-aws-sdk/src/base.js +13 -9
  44. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +43 -6
  45. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +83 -91
  46. package/packages/datadog-plugin-cucumber/src/index.js +1 -1
  47. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +19 -5
  48. package/packages/datadog-plugin-cypress/src/support.js +45 -1
  49. package/packages/datadog-plugin-express/src/code_origin.js +1 -1
  50. package/packages/datadog-plugin-fastify/src/code_origin.js +1 -1
  51. package/packages/datadog-plugin-graphql/src/execute.js +627 -11
  52. package/packages/datadog-plugin-graphql/src/index.js +20 -8
  53. package/packages/datadog-plugin-graphql/src/parse.js +24 -4
  54. package/packages/datadog-plugin-graphql/src/validate.js +16 -5
  55. package/packages/datadog-plugin-http/src/client.js +8 -3
  56. package/packages/datadog-plugin-http/src/index.js +6 -8
  57. package/packages/datadog-plugin-http2/src/client.js +5 -2
  58. package/packages/datadog-plugin-jest/src/index.js +1 -1
  59. package/packages/datadog-plugin-mocha/src/index.js +1 -1
  60. package/packages/datadog-plugin-mysql/src/index.js +7 -2
  61. package/packages/datadog-plugin-openai/src/services.js +2 -2
  62. package/packages/datadog-plugin-playwright/src/index.js +1 -1
  63. package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +2 -2
  64. package/packages/datadog-plugin-undici/src/index.js +5 -2
  65. package/packages/datadog-plugin-vitest/src/index.js +76 -64
  66. package/packages/datadog-shimmer/src/shimmer.js +37 -16
  67. package/packages/datadog-webpack/index.js +17 -1
  68. package/packages/datadog-webpack/src/optional-peer-loader.js +17 -0
  69. package/packages/dd-trace/src/aiguard/sdk.js +15 -3
  70. package/packages/dd-trace/src/appsec/api_security/index.js +55 -0
  71. package/packages/dd-trace/src/appsec/api_security/sampler.js +147 -0
  72. package/packages/dd-trace/src/appsec/channels.js +3 -1
  73. package/packages/dd-trace/src/appsec/downstream_requests.js +4 -4
  74. package/packages/dd-trace/src/appsec/graphql.js +5 -5
  75. package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +26 -19
  76. package/packages/dd-trace/src/appsec/iast/security-controls/index.js +2 -2
  77. package/packages/dd-trace/src/appsec/iast/telemetry/index.js +2 -1
  78. package/packages/dd-trace/src/appsec/index.js +20 -7
  79. package/packages/dd-trace/src/appsec/lambda.js +135 -0
  80. package/packages/dd-trace/src/appsec/reporter.js +50 -10
  81. package/packages/dd-trace/src/appsec/telemetry/api_security.js +34 -0
  82. package/packages/dd-trace/src/appsec/telemetry/index.js +27 -1
  83. package/packages/dd-trace/src/appsec/waf/index.js +16 -4
  84. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +4 -4
  85. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +21 -6
  86. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +2 -2
  87. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +1 -1
  88. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +1 -1
  89. package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +3 -2
  90. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +1 -1
  91. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +55 -13
  92. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +2 -2
  93. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +17 -0
  94. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +47 -26
  95. package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +2 -2
  96. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +64 -53
  97. package/packages/dd-trace/src/ci-visibility/requests/request.js +1 -17
  98. package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +3 -3
  99. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +23 -5
  100. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +128 -0
  101. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +287 -0
  102. package/packages/dd-trace/src/config/defaults.js +25 -5
  103. package/packages/dd-trace/src/config/generated-config-types.d.ts +596 -58
  104. package/packages/dd-trace/src/config/helper.js +134 -33
  105. package/packages/dd-trace/src/config/index.js +39 -23
  106. package/packages/dd-trace/src/config/major-overrides.js +4 -2
  107. package/packages/dd-trace/src/config/parsers.js +19 -1
  108. package/packages/dd-trace/src/config/remote_config.js +1 -1
  109. package/packages/dd-trace/src/config/supported-configurations.json +109 -57
  110. package/packages/dd-trace/src/constants.js +7 -0
  111. package/packages/dd-trace/src/datastreams/processor.js +11 -3
  112. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +47 -35
  113. package/packages/dd-trace/src/debugger/devtools_client/index.js +31 -66
  114. package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +83 -0
  115. package/packages/dd-trace/src/debugger/devtools_client/state.js +1 -0
  116. package/packages/dd-trace/src/debugger/index.js +5 -0
  117. package/packages/dd-trace/src/debugger/probe_sampler.js +112 -0
  118. package/packages/dd-trace/src/debugger/probe_sampler_constants.js +19 -0
  119. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +77 -26
  120. package/packages/dd-trace/src/exporters/agent/index.js +1 -1
  121. package/packages/dd-trace/src/exporters/agentless/writer.js +4 -4
  122. package/packages/dd-trace/src/exporters/common/request.js +1 -20
  123. package/packages/dd-trace/src/exporters/common/url.js +34 -0
  124. package/packages/dd-trace/src/exporters/span-stats/writer.js +1 -3
  125. package/packages/dd-trace/src/guardrails/index.js +8 -5
  126. package/packages/dd-trace/src/index.js +6 -4
  127. package/packages/dd-trace/src/llmobs/constants/tags.js +6 -0
  128. package/packages/dd-trace/src/llmobs/index.js +17 -6
  129. package/packages/dd-trace/src/llmobs/plugins/base.js +4 -4
  130. package/packages/dd-trace/src/llmobs/plugins/genai/util.js +3 -3
  131. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +5 -1
  132. package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
  133. package/packages/dd-trace/src/llmobs/sdk.js +8 -6
  134. package/packages/dd-trace/src/llmobs/span_processor.js +5 -1
  135. package/packages/dd-trace/src/llmobs/tagger.js +58 -3
  136. package/packages/dd-trace/src/llmobs/telemetry.js +1 -0
  137. package/packages/dd-trace/src/llmobs/writers/base.js +1 -1
  138. package/packages/dd-trace/src/log/index.js +18 -20
  139. package/packages/dd-trace/src/openfeature/flagging_provider.js +2 -7
  140. package/packages/dd-trace/src/opentelemetry/bridge-span-base.js +5 -4
  141. package/packages/dd-trace/src/opentelemetry/metrics/index.js +54 -5
  142. package/packages/dd-trace/src/opentelemetry/metrics/instruments.js +2 -1
  143. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +40 -0
  144. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +169 -0
  145. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +4 -3
  146. package/packages/dd-trace/src/opentelemetry/metrics/time.js +19 -0
  147. package/packages/dd-trace/src/opentelemetry/otlp/common.proto +2 -2
  148. package/packages/dd-trace/src/opentelemetry/otlp/logs.proto +1 -1
  149. package/packages/dd-trace/src/opentelemetry/otlp/logs_service.proto +1 -1
  150. package/packages/dd-trace/src/opentelemetry/otlp/metrics.proto +1 -1
  151. package/packages/dd-trace/src/opentelemetry/otlp/metrics_service.proto +1 -1
  152. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +44 -38
  153. package/packages/dd-trace/src/opentelemetry/span-helpers.js +29 -13
  154. package/packages/dd-trace/src/opentelemetry/span.js +14 -3
  155. package/packages/dd-trace/src/opentelemetry/span_context.js +1 -0
  156. package/packages/dd-trace/src/opentelemetry/trace/index.js +6 -1
  157. package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +3 -2
  158. package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +26 -13
  159. package/packages/dd-trace/src/opentelemetry/tracer_provider.js +1 -1
  160. package/packages/dd-trace/src/opentracing/propagation/text_map.js +3 -3
  161. package/packages/dd-trace/src/opentracing/span_context.js +17 -0
  162. package/packages/dd-trace/src/opentracing/tracer.js +6 -1
  163. package/packages/dd-trace/src/plugin_manager.js +14 -8
  164. package/packages/dd-trace/src/plugins/ci_plugin.js +21 -4
  165. package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +276 -0
  166. package/packages/dd-trace/src/plugins/util/llm.js +6 -1
  167. package/packages/dd-trace/src/plugins/util/test.js +91 -9
  168. package/packages/dd-trace/src/plugins/util/url.js +68 -1
  169. package/packages/dd-trace/src/plugins/util/web.js +19 -36
  170. package/packages/dd-trace/src/priority_sampler.js +1 -13
  171. package/packages/dd-trace/src/profiling/config.js +129 -198
  172. package/packages/dd-trace/src/profiling/constants.js +4 -7
  173. package/packages/dd-trace/src/profiling/exporter_cli.js +10 -10
  174. package/packages/dd-trace/src/profiling/exporters/agent.js +23 -16
  175. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +45 -14
  176. package/packages/dd-trace/src/profiling/exporters/file.js +16 -5
  177. package/packages/dd-trace/src/profiling/oom.js +71 -0
  178. package/packages/dd-trace/src/profiling/profiler.js +35 -23
  179. package/packages/dd-trace/src/profiling/profilers/events.js +14 -7
  180. package/packages/dd-trace/src/profiling/profilers/space.js +27 -18
  181. package/packages/dd-trace/src/profiling/profilers/wall.js +35 -25
  182. package/packages/dd-trace/src/proxy.js +8 -8
  183. package/packages/dd-trace/src/ritm.js +4 -2
  184. package/packages/dd-trace/src/serverless.js +1 -2
  185. package/packages/dd-trace/src/service-naming/schemas/util.js +36 -5
  186. package/packages/dd-trace/src/service-naming/schemas/v1/web.js +5 -3
  187. package/packages/dd-trace/src/span_format.js +2 -1
  188. package/packages/dd-trace/src/span_processor.js +11 -6
  189. package/packages/dd-trace/src/span_stats.js +96 -78
  190. package/packages/dd-trace/src/startup-log.js +2 -1
  191. package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
  192. package/packages/dd-trace/src/telemetry/endpoints.js +6 -4
  193. package/packages/dd-trace/src/telemetry/index.js +2 -2
  194. package/packages/dd-trace/src/telemetry/logs/index.js +1 -1
  195. package/packages/dd-trace/src/telemetry/send-data.js +20 -18
  196. package/packages/dd-trace/src/telemetry/session-propagation.js +1 -1
  197. package/packages/dd-trace/src/telemetry/telemetry.js +9 -9
  198. package/packages/dd-trace/src/tracer.js +15 -1
  199. package/packages/dd-trace/src/util.js +14 -0
  200. package/register.js +58 -1
  201. package/vendor/dist/@apm-js-collab/code-transformer/index.js +1 -1
  202. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  203. package/vendor/dist/pprof-format/index.js +1 -1
  204. package/vendor/dist/protobufjs/index.js +1 -1
  205. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  206. package/vendor/dist/shell-quote/index.js +1 -1
  207. package/packages/datadog-plugin-graphql/src/resolve.js +0 -170
  208. package/packages/dd-trace/src/appsec/api_security_sampler.js +0 -117
@@ -9,6 +9,7 @@ const {
9
9
  finishAllTraceSpans,
10
10
  getTestSuitePath,
11
11
  getTestSuiteCommonTags,
12
+ getTestLevelsMetadataTags,
12
13
  getTestSessionName,
13
14
  getIsFaultyEarlyFlakeDetection,
14
15
  TEST_SOURCE_FILE,
@@ -16,6 +17,7 @@ const {
16
17
  TEST_CODE_COVERAGE_LINES_PCT,
17
18
  TEST_CODE_OWNERS,
18
19
  TEST_COMMAND,
20
+ TEST_LEVELS_METADATA,
19
21
  TEST_SESSION_NAME,
20
22
  TEST_SOURCE_START,
21
23
  TEST_IS_NEW,
@@ -28,12 +30,12 @@ const {
28
30
  TEST_MANAGEMENT_IS_ATTEMPT_TO_FIX,
29
31
  TEST_MANAGEMENT_ATTEMPT_TO_FIX_PASSED,
30
32
  TEST_HAS_FAILED_ALL_RETRIES,
31
- getLibraryCapabilitiesTags,
33
+ getLibraryCapabilitiesTags: getDefaultLibraryCapabilitiesTags,
32
34
  TEST_RETRY_REASON_TYPES,
33
- isModifiedTest,
34
35
  TEST_IS_MODIFIED,
35
36
  TEST_HAS_DYNAMIC_NAME,
36
37
  TEST_FINAL_STATUS,
38
+ TEST_IS_TEST_FRAMEWORK_WORKER,
37
39
  } = require('../../dd-trace/src/plugins/util/test')
38
40
  const { COMPONENT } = require('../../dd-trace/src/constants')
39
41
  const {
@@ -68,49 +70,6 @@ class VitestPlugin extends CiPlugin {
68
70
  })
69
71
  })
70
72
 
71
- this.addSub('ci:vitest:test:is-new', ({ knownTests, testSuiteAbsolutePath, testName, onDone }) => {
72
- // if for whatever reason the worker does not receive valid known tests, we don't consider it as new
73
- if (!knownTests.vitest) {
74
- return onDone(false)
75
- }
76
- const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.repositoryRoot)
77
- const testsForThisTestSuite = knownTests.vitest[testSuite] || []
78
- onDone(!testsForThisTestSuite.includes(testName))
79
- })
80
-
81
- this.addSub('ci:vitest:test:is-attempt-to-fix', ({
82
- testManagementTests,
83
- testSuiteAbsolutePath,
84
- testName,
85
- onDone,
86
- }) => {
87
- const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.repositoryRoot)
88
- const { isAttemptToFix } = this.getTestProperties(testManagementTests, testSuite, testName)
89
-
90
- onDone(isAttemptToFix ?? false)
91
- })
92
-
93
- this.addSub('ci:vitest:test:is-disabled', ({ testManagementTests, testSuiteAbsolutePath, testName, onDone }) => {
94
- const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.repositoryRoot)
95
- const { isDisabled } = this.getTestProperties(testManagementTests, testSuite, testName)
96
-
97
- onDone(isDisabled)
98
- })
99
-
100
- this.addSub('ci:vitest:test:is-quarantined', ({ testManagementTests, testSuiteAbsolutePath, testName, onDone }) => {
101
- const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.repositoryRoot)
102
- const { isQuarantined } = this.getTestProperties(testManagementTests, testSuite, testName)
103
-
104
- onDone(isQuarantined)
105
- })
106
-
107
- this.addSub('ci:vitest:test:is-modified', ({ modifiedFiles, testSuiteAbsolutePath, onDone }) => {
108
- const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.repositoryRoot)
109
- const isModified = isModifiedTest(testSuite, 0, 0, modifiedFiles, this.constructor.id)
110
-
111
- onDone(isModified)
112
- })
113
-
114
73
  this.addSub('ci:vitest:is-early-flake-detection-faulty', ({
115
74
  knownTests,
116
75
  testFilepaths,
@@ -139,12 +98,16 @@ class VitestPlugin extends CiPlugin {
139
98
  isRetryReasonAttemptToFix,
140
99
  isRetryReasonAtr,
141
100
  isModified,
101
+ isTestFrameworkWorker,
102
+ requestErrorTags,
142
103
  } = ctx
143
104
 
144
105
  const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.repositoryRoot)
145
- const store = storage('legacy').getStore()
106
+ const store = ctx.currentStore || storage('legacy').getStore()
107
+ const testSuiteSpan = store?.testSuiteSpan || this.testSuiteSpan
146
108
 
147
109
  const extraTags = {
110
+ ...requestErrorTags,
148
111
  [TEST_SOURCE_FILE]: testSuite,
149
112
  }
150
113
  if (isRetry) {
@@ -177,11 +140,14 @@ class VitestPlugin extends CiPlugin {
177
140
  if (isModified) {
178
141
  extraTags[TEST_IS_MODIFIED] = 'true'
179
142
  }
143
+ if (isTestFrameworkWorker) {
144
+ extraTags[TEST_IS_TEST_FRAMEWORK_WORKER] = 'true'
145
+ }
180
146
 
181
147
  const span = this.startTestSpan(
182
148
  testName,
183
149
  testSuite,
184
- this.testSuiteSpan,
150
+ testSuiteSpan,
185
151
  extraTags
186
152
  )
187
153
 
@@ -202,7 +168,7 @@ class VitestPlugin extends CiPlugin {
202
168
  })
203
169
 
204
170
  this.addBind('ci:vitest:test:finish-time', (ctx) => {
205
- const { status, task, attemptToFixPassed, attemptToFixFailed } = ctx
171
+ const { status, task, attemptToFixPassed, attemptToFixFailed, duration } = ctx
206
172
  const span = ctx.currentStore?.span
207
173
 
208
174
  // we store the finish time to finish at a later hook
@@ -216,7 +182,8 @@ class VitestPlugin extends CiPlugin {
216
182
  span.setTag(TEST_MANAGEMENT_ATTEMPT_TO_FIX_PASSED, 'false')
217
183
  }
218
184
 
219
- this.taskToFinishTime.set(task, span._getTime())
185
+ const finishTime = typeof duration === 'number' ? span._startTime + duration : span._getTime()
186
+ this.taskToFinishTime.set(task, finishTime)
220
187
 
221
188
  ctx.parentStore = ctx.currentStore
222
189
  ctx.currentStore = { ...ctx.currentStore, span }
@@ -289,19 +256,29 @@ class VitestPlugin extends CiPlugin {
289
256
  finishAllTraceSpans(span)
290
257
  })
291
258
 
292
- this.addSub('ci:vitest:test:skip', ({ testName, testSuiteAbsolutePath, isNew, isDisabled }) => {
259
+ this.addSub('ci:vitest:test:skip', ({
260
+ testName,
261
+ testSuiteAbsolutePath,
262
+ isNew,
263
+ isDisabled,
264
+ isTestFrameworkWorker,
265
+ requestErrorTags,
266
+ testSuiteSpan,
267
+ }) => {
293
268
  const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.repositoryRoot)
294
269
  const testSpan = this.startTestSpan(
295
270
  testName,
296
271
  testSuite,
297
- this.testSuiteSpan,
272
+ testSuiteSpan || this.testSuiteSpan,
298
273
  {
274
+ ...requestErrorTags,
299
275
  [TEST_SOURCE_FILE]: testSuite,
300
276
  [TEST_SOURCE_START]: 1, // we can't get the proper start line in vitest
301
277
  [TEST_STATUS]: 'skip',
302
278
  [TEST_FINAL_STATUS]: 'skip',
303
279
  ...(isDisabled ? { [TEST_MANAGEMENT_IS_DISABLED]: 'true' } : {}),
304
280
  ...(isNew ? { [TEST_IS_NEW]: 'true' } : {}),
281
+ ...(isTestFrameworkWorker ? { [TEST_IS_TEST_FRAMEWORK_WORKER]: 'true' } : {}),
305
282
  }
306
283
  )
307
284
  this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'test', this.getTestTelemetryTags(testSpan))
@@ -309,7 +286,15 @@ class VitestPlugin extends CiPlugin {
309
286
  })
310
287
 
311
288
  this.addBind('ci:vitest:test-suite:start', (ctx) => {
312
- const { codeOwnersEntries, repositoryRoot, testSuiteAbsolutePath, frameworkVersion } = ctx
289
+ const {
290
+ codeOwnersEntries,
291
+ repositoryRoot,
292
+ requestErrorTags,
293
+ testSuiteAbsolutePath,
294
+ frameworkVersion,
295
+ isTestFrameworkWorker,
296
+ isVitestNoWorkerInitActive,
297
+ } = ctx
313
298
 
314
299
  const testCommand = ctx.testCommand || 'vitest run'
315
300
  const { testSessionId, testModuleId } = ctx
@@ -327,9 +312,16 @@ class VitestPlugin extends CiPlugin {
327
312
  // test suites run in a different process, so they also need to init the metadata dictionary
328
313
  const testSessionName = getTestSessionName(this.config, trimmedCommand, this.testEnvironmentMetadata)
329
314
  if (this.tracer._exporter.addMetadataTags) {
315
+ const libraryCapabilitiesTags = this.getLibraryCapabilitiesTags(frameworkVersion, {
316
+ isVitestNoWorkerInitActive,
317
+ })
330
318
  this.tracer._exporter.addMetadataTags({
331
- '*': { [TEST_COMMAND]: testCommand, [TEST_SESSION_NAME]: testSessionName },
332
- test: getLibraryCapabilitiesTags(this.constructor.id),
319
+ [TEST_LEVELS_METADATA]: {
320
+ [TEST_COMMAND]: testCommand,
321
+ [TEST_SESSION_NAME]: testSessionName,
322
+ ...getTestLevelsMetadataTags(this.testEnvironmentMetadata),
323
+ },
324
+ test: libraryCapabilitiesTags,
333
325
  })
334
326
  }
335
327
 
@@ -342,9 +334,13 @@ class VitestPlugin extends CiPlugin {
342
334
  testSuite,
343
335
  'vitest'
344
336
  ),
337
+ ...requestErrorTags,
345
338
  [TEST_SOURCE_FILE]: testSuite,
346
339
  [TEST_SOURCE_START]: 1,
347
340
  }
341
+ if (isTestFrameworkWorker) {
342
+ testSuiteMetadata[TEST_IS_TEST_FRAMEWORK_WORKER] = 'true'
343
+ }
348
344
 
349
345
  const codeOwners = this.getCodeOwners(testSuiteMetadata)
350
346
  if (codeOwners) {
@@ -368,13 +364,17 @@ class VitestPlugin extends CiPlugin {
368
364
  return ctx.currentStore
369
365
  })
370
366
 
371
- this.addSub('ci:vitest:test-suite:finish', ({ testSuiteSpan, status, onFinish }) => {
367
+ this.addSub('ci:vitest:test-suite:finish', ({ testSuiteSpan, status, deferFlush, onFinish }) => {
372
368
  if (testSuiteSpan) {
373
369
  testSuiteSpan.setTag(TEST_STATUS, status)
374
370
  testSuiteSpan.finish()
375
371
  finishAllTraceSpans(testSuiteSpan)
376
372
  }
377
373
  this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'suite')
374
+ if (deferFlush) {
375
+ onFinish()
376
+ return
377
+ }
378
378
  this.tracer._exporter.flush(onFinish)
379
379
  if (this.runningTestProbe) {
380
380
  this.removeDiProbe(this.runningTestProbe)
@@ -403,9 +403,15 @@ class VitestPlugin extends CiPlugin {
403
403
  isEarlyFlakeDetectionEnabled,
404
404
  isEarlyFlakeDetectionFaulty,
405
405
  isTestManagementTestsEnabled,
406
+ requestErrorTags,
406
407
  vitestPool,
408
+ isVitestNoWorkerInitActive,
407
409
  onFinish,
408
410
  }) => {
411
+ for (const [tag, value] of Object.entries(requestErrorTags || {})) {
412
+ this.testSessionSpan.setTag(tag, value)
413
+ this.testModuleSpan.setTag(tag, value)
414
+ }
409
415
  this.testSessionSpan.setTag(TEST_STATUS, status)
410
416
  this.testModuleSpan.setTag(TEST_STATUS, status)
411
417
  if (error) {
@@ -432,12 +438,12 @@ class VitestPlugin extends CiPlugin {
432
438
  this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'module')
433
439
  this.testSessionSpan.finish()
434
440
  this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'session', {
435
- hasFailedTestReplay: this.libraryConfig?.isDiEnabled || undefined,
441
+ hasFailedTestReplay: this.libraryConfig?.isDiEnabled && !isVitestNoWorkerInitActive ? true : undefined,
436
442
  })
437
443
  finishAllTraceSpans(this.testSessionSpan)
438
444
  this.telemetry.count(TELEMETRY_TEST_SESSION, {
439
445
  provider: this.ciProviderName,
440
- autoInjected: !!this._tracerConfig.DD_CIVISIBILITY_AUTO_INSTRUMENTATION_PROVIDER,
446
+ autoInjected: !!this._tracerConfig.testOptimization.DD_CIVISIBILITY_AUTO_INSTRUMENTATION_PROVIDER,
441
447
  })
442
448
  this.tracer._exporter.flush(onFinish)
443
449
  })
@@ -447,6 +453,19 @@ class VitestPlugin extends CiPlugin {
447
453
  })
448
454
  }
449
455
 
456
+ /**
457
+ * Returns Vitest library capability metadata tags.
458
+ * @param {string} frameworkVersion - The Vitest version.
459
+ * @param {object} [ctx] - Diagnostic channel context.
460
+ * @param {boolean} [ctx.isVitestNoWorkerInitActive] - Whether no-worker init is active for this run.
461
+ * @returns {Record<string, string|undefined>}
462
+ */
463
+ getLibraryCapabilitiesTags (frameworkVersion, ctx = {}) {
464
+ return getDefaultLibraryCapabilitiesTags(this.constructor.id, frameworkVersion, {
465
+ omitFailedTestReplay: ctx.isVitestNoWorkerInitActive,
466
+ })
467
+ }
468
+
450
469
  /**
451
470
  * Handles the coverage report by discovering and uploading it if enabled.
452
471
  * @param {string} rootDir - The root directory where coverage reports are located.
@@ -463,13 +482,6 @@ class VitestPlugin extends CiPlugin {
463
482
  onDone,
464
483
  })
465
484
  }
466
-
467
- getTestProperties (testManagementTests, testSuite, testName) {
468
- const { attempt_to_fix: isAttemptToFix, disabled: isDisabled, quarantined: isQuarantined } =
469
- testManagementTests?.vitest?.suites?.[testSuite]?.tests?.[testName]?.properties || {}
470
-
471
- return { isAttemptToFix, isDisabled, isQuarantined }
472
- }
473
485
  }
474
486
 
475
487
  module.exports = VitestPlugin
@@ -119,11 +119,14 @@ function wrapCallback (original, wrapper) {
119
119
  * object.
120
120
  * @param {string | symbol} name - The property key of the method to wrap.
121
121
  * @param {(original: Function) => (...args: unknown[]) => unknown} wrapper - The wrapper function.
122
- * @param {{ replaceGetter?: boolean }} [options] - If `replaceGetter` is set to
123
- * true, the getter is accessed and the getter is replaced with one that just
124
- * returns the earlier retrieved value. Use with care! This may only be done in
125
- * case the getter absolutely has no side effect and no setter is defined for the
126
- * property.
122
+ * @param {{ replaceGetter?: boolean }} [options] - By default the getter is
123
+ * wrapped in place, so each property access runs the wrapper. A getter+setter
124
+ * pair keeps its setter; a setter-only property throws. If `replaceGetter` is
125
+ * true, the getter is instead accessed once and replaced with one returning the
126
+ * resolved wrapped value — for a lazy getter+setter pair (e.g. Node 20's
127
+ * `fs.opendir`) the setter is rebuilt to materialize a writable data property on
128
+ * assignment, keeping the descriptor observationally identical for downstream
129
+ * consumers. Use with care! This may only be done when the getter has no side effect.
127
130
  * @returns {Record<string | symbol, unknown> | Function | undefined} The target object with
128
131
  * the wrapped method.
129
132
  */
@@ -151,17 +154,13 @@ function wrap (target, name, wrapper, options) {
151
154
  enumerable: false,
152
155
  }
153
156
 
154
- if (descriptor.set && (!descriptor.get || options?.replaceGetter)) {
155
- // It is possible to support these cases by instrumenting both the getter
156
- // and setter (or only the setter, in case that is a use case).
157
- // For now, this is not supported due to the complexity and the fact that
158
- // this is not a common use case.
159
- throw new Error(options?.replaceGetter
160
- ? 'Replacing a getter/setter pair is not supported. Implement if required.'
161
- : 'Replacing setters is not supported. Implement if required.')
157
+ // A setter-only property has nothing to wrap. Instrumenting the setter is not
158
+ // implemented; a getter+setter pair is handled below.
159
+ if (descriptor.set && !descriptor.get) {
160
+ throw new Error('Replacing setters is not supported. Implement if required.')
162
161
  }
163
162
 
164
- const original = descriptor.value ?? options?.replaceGetter ? target[name] : descriptor.get
163
+ const original = (descriptor.value ?? options?.replaceGetter) ? target[name] : descriptor.get
165
164
 
166
165
  assertMethod(target, name, original)
167
166
 
@@ -176,8 +175,30 @@ function wrap (target, name, wrapper, options) {
176
175
  }
177
176
  descriptor.value = wrapped
178
177
  } else {
179
- if (descriptor.get) {
180
- // `replaceGetter` may only be used when the getter has no side effect.
178
+ if (descriptor.set && options?.replaceGetter) {
179
+ // A lazy accessor pair (e.g. Node 20's `fs.opendir`). `original` already
180
+ // resolved the value through the getter. Keep the property an accessor pair
181
+ // so the shape stays observationally identical — a downstream consumer may
182
+ // read the descriptor or assign to it on a specific Node.js version. The
183
+ // getter returns the wrapped value; the setter mirrors the native lazy
184
+ // contract (assignment self-replaces the property with a writable data
185
+ // property holding the assigned value), so a caller that overwrites the
186
+ // method gets exactly what they set, unwrapped, as before. The descriptor
187
+ // is the original accessor descriptor (no `value`/`writable` slots), so
188
+ // reassigning `get`/`set` keeps it a valid accessor descriptor.
189
+ descriptor.get = () => wrapped
190
+ descriptor.set = function (value) {
191
+ Object.defineProperty(this, name, {
192
+ configurable: descriptor.configurable,
193
+ enumerable: descriptor.enumerable,
194
+ writable: true,
195
+ value,
196
+ })
197
+ }
198
+ } else if (descriptor.get) {
199
+ // Wrap the getter in place; for a getter+setter pair the original setter
200
+ // stays untouched. `replaceGetter` (no side effect on read) instead returns
201
+ // the value resolved once into `wrapped`.
181
202
  descriptor.get = options?.replaceGetter ? () => wrapped : wrapped
182
203
  } else {
183
204
  descriptor.value = wrapped
@@ -6,6 +6,7 @@ const fs = require('node:fs')
6
6
  const instrumentations = require('../datadog-instrumentations/src/helpers/instrumentations')
7
7
  const extractPackageAndModulePath = require('../datadog-instrumentations/src/helpers/extract-package-and-module-path')
8
8
  const hooks = require('../datadog-instrumentations/src/helpers/hooks')
9
+ const { matchesOptionalPeerFile } = require('../datadog-instrumentations/src/helpers/optional-peer-bundler')
9
10
  const { isESMFile } = require('../datadog-esbuild/src/utils')
10
11
  const log = require('./src/log')
11
12
 
@@ -130,11 +131,26 @@ class DatadogWebpackPlugin {
130
131
  nmf.hooks.afterResolve.tap(PLUGIN_NAME, (resolveData) => {
131
132
  const { createData } = resolveData
132
133
  const resource = createData?.resource
133
- if (!resource || !resource.includes('node_modules')) {
134
+ if (!resource) {
134
135
  return
135
136
  }
136
137
 
137
138
  const normalizedResource = resource.replaceAll('\\', '/')
139
+
140
+ // Rewrite optional-peer loads so installed peers get bundled and survive relocation
141
+ // (#8980); absent peers stay opaque, so a build that does not opt into the feature does
142
+ // not follow their dependency chain (#8635).
143
+ if (matchesOptionalPeerFile(normalizedResource)) {
144
+ createData.loaders = createData.loaders || []
145
+ createData.loaders.push({ loader: require.resolve('./src/optional-peer-loader') })
146
+ log.debug('INLINE: optional-peer loader applied to %s', normalizedResource)
147
+ return
148
+ }
149
+
150
+ if (!resource.includes('node_modules')) {
151
+ return
152
+ }
153
+
138
154
  const { pkg, path: modulePath, pkgJson } = extractPackageAndModulePath(normalizedResource)
139
155
  if (!pkg) {
140
156
  return
@@ -0,0 +1,17 @@
1
+ 'use strict'
2
+
3
+ const { rewriteOptionalPeerLoads } = require('../../datadog-instrumentations/src/helpers/optional-peer-bundler')
4
+
5
+ /**
6
+ * Webpack loader applied to the optional-peer loader files. Rewrites each
7
+ * `requireOptionalPeer('name')` whose peer is installed at build time into a literal
8
+ * `require('name')` so webpack bundles the peer (#8980). Peers that are absent stay opaque, so
9
+ * builds that do not opt into the feature keep the #8635 guarantee.
10
+ *
11
+ * @param {string} source
12
+ * @returns {string}
13
+ */
14
+ module.exports = function optionalPeerLoader (source) {
15
+ this.cacheable(false)
16
+ return rewriteOptionalPeerLoads(source, this.context)
17
+ }
@@ -63,6 +63,18 @@ class AIGuardClientError extends Error {
63
63
  }
64
64
  }
65
65
 
66
+ /**
67
+ * Resolves the AI Guard host for a given Datadog site. Sites with a single subdomain level
68
+ * (e.g. `datadoghq.com`, `ddog-gov.com`) are served from the `app.` subdomain, while regional
69
+ * sites (e.g. `us3.datadoghq.com`, `ap1.datadoghq.com`) are used as-is.
70
+ *
71
+ * @param {string} site - Datadog site (e.g. `datadoghq.com`, `us3.datadoghq.com`)
72
+ * @returns {string} The host to use for the AI Guard endpoint
73
+ */
74
+ function aiGuardHost (site) {
75
+ return site.split('.').length === 2 ? `app.${site}` : site
76
+ }
77
+
66
78
  class AIGuard extends NoopAIGuard {
67
79
  #initialized
68
80
  #tracer
@@ -81,20 +93,20 @@ class AIGuard extends NoopAIGuard {
81
93
  constructor (tracer, config) {
82
94
  super()
83
95
 
84
- if (!config.apiKey || !config.DD_APP_KEY) {
96
+ if (!config.DD_API_KEY || !config.DD_APP_KEY) {
85
97
  log.error('AIGuard: missing api and/or app keys, use env DD_API_KEY and DD_APP_KEY')
86
98
  this.#initialized = false
87
99
  return
88
100
  }
89
101
  this.#tracer = tracer
90
102
  this.#headers = {
91
- 'DD-API-KEY': config.apiKey,
103
+ 'DD-API-KEY': config.DD_API_KEY,
92
104
  'DD-APPLICATION-KEY': config.DD_APP_KEY,
93
105
  'DD-AI-GUARD-VERSION': tracerVersion,
94
106
  'DD-AI-GUARD-SOURCE': 'SDK',
95
107
  'DD-AI-GUARD-LANGUAGE': 'nodejs',
96
108
  }
97
- const endpoint = config.experimental.aiguard.endpoint || `https://app.${config.site}/api/v2/ai-guard`
109
+ const endpoint = config.experimental.aiguard.endpoint || `https://${aiGuardHost(config.site)}/api/v2/ai-guard`
98
110
  this.#evaluateUrl = `${endpoint}/evaluate`
99
111
  this.#timeout = config.experimental.aiguard.timeout
100
112
  this.#maxMessagesLength = config.experimental.aiguard.maxMessagesLength
@@ -0,0 +1,55 @@
1
+ 'use strict'
2
+
3
+ const web = require('../../plugins/util/web')
4
+ const { isSchemaAttribute } = require('../reporter')
5
+ const appsecTelemetry = require('../telemetry')
6
+ const sampler = require('./sampler')
7
+
8
+ /**
9
+ * Map a sampling decision into the corresponding API Security telemetry metric.
10
+ *
11
+ * The decision is done by the sampler, here the outcome is translated to emitted metrics:
12
+ * - SAMPLE: request.schema / request.no_schema depending on WAF schema attributes
13
+ * - MISSING_ROUTE: missing_route
14
+ * - SKIP: no metric emitted
15
+ *
16
+ * @param {import('http').IncomingMessage} req
17
+ * @param {'sample' | 'missing_route' | 'skip'} samplingDecision Sampler decision
18
+ * @param {{ attributes?: Record<string, unknown> } | undefined} wafResult WAF run result
19
+ */
20
+ function reportRequest (req, samplingDecision, wafResult) {
21
+ switch (samplingDecision) {
22
+ case sampler.SamplingDecision.SAMPLE: {
23
+ const framework = getFramework(req)
24
+ if (hasSchemaAttributes(wafResult?.attributes)) {
25
+ appsecTelemetry.incrementApiSecRequestSchemaMetric(framework)
26
+ } else {
27
+ appsecTelemetry.incrementApiSecRequestNoSchemaMetric(framework)
28
+ }
29
+ break
30
+ }
31
+ case sampler.SamplingDecision.MISSING_ROUTE:
32
+ appsecTelemetry.incrementApiSecMissingRouteMetric(getFramework(req))
33
+ break
34
+ }
35
+ }
36
+
37
+ function getFramework (req) {
38
+ return web.root(req)?.context()?.getTag?.('component')
39
+ }
40
+
41
+ function hasSchemaAttributes (attributes) {
42
+ if (!attributes) return false
43
+ for (const key of Object.keys(attributes)) {
44
+ if (isSchemaAttribute(key)) return true
45
+ }
46
+ return false
47
+ }
48
+
49
+ module.exports = {
50
+ configure: sampler.configure,
51
+ disable: sampler.disable,
52
+ sampleRequest: sampler.sampleRequest,
53
+ reportRequest,
54
+ SamplingDecision: sampler.SamplingDecision,
55
+ }
@@ -0,0 +1,147 @@
1
+ 'use strict'
2
+
3
+ const { TTLCache } = require('../../../../../vendor/dist/@isaacs/ttlcache')
4
+ const web = require('../../plugins/util/web')
5
+ const log = require('../../log')
6
+ const { AUTO_REJECT, USER_REJECT } = require('../../../../../ext/priority')
7
+ const { keepTrace } = require('../../priority_sampler')
8
+ const { ASM } = require('../../standalone/product')
9
+ const { isBlocked } = require('../blocking')
10
+
11
+ const MAX_SIZE = 4096
12
+
13
+ const SamplingDecision = Object.freeze({
14
+ SAMPLE: 'sample',
15
+ MISSING_ROUTE: 'missing_route',
16
+ SKIP: 'skip',
17
+ })
18
+
19
+ let enabled
20
+ let asmStandaloneEnabled
21
+
22
+ /**
23
+ * @type {TTLCache}
24
+ */
25
+ let sampledRequests
26
+
27
+ class NoopTTLCache {
28
+ clear () {}
29
+ set (_key, _value) {}
30
+ has (_key) { return false }
31
+ }
32
+
33
+ function configure ({ appsec, apmTracingEnabled }) {
34
+ enabled = appsec.DD_API_SECURITY_ENABLED
35
+ asmStandaloneEnabled = apmTracingEnabled === false
36
+ sampledRequests = appsec.DD_API_SECURITY_SAMPLE_DELAY === 0
37
+ ? new NoopTTLCache()
38
+ : new TTLCache({ max: MAX_SIZE, ttl: appsec.DD_API_SECURITY_SAMPLE_DELAY * 1000 })
39
+ }
40
+
41
+ function disable () {
42
+ enabled = false
43
+ sampledRequests?.clear()
44
+ }
45
+
46
+ /**
47
+ * @param {import('http').IncomingMessage} req
48
+ * @param {import('http').ServerResponse} res
49
+ * @param {boolean} record When true and the decision is SAMPLE, records the endpoint in the TTL cache
50
+ * @returns {'sample' | 'missing_route' | 'skip'}
51
+ */
52
+ function sampleRequest (req, res, record = false) {
53
+ if (!enabled) return SamplingDecision.SKIP
54
+
55
+ const rootSpan = web.root(req)
56
+ if (!rootSpan) return SamplingDecision.SKIP
57
+
58
+ if (!asmStandaloneEnabled) {
59
+ let priority = getSpanPriority(rootSpan)
60
+ if (!priority) {
61
+ rootSpan._prioritySampler?.sample(rootSpan)
62
+ priority = getSpanPriority(rootSpan)
63
+ }
64
+
65
+ if (priority === AUTO_REJECT || priority === USER_REJECT) {
66
+ return SamplingDecision.SKIP
67
+ }
68
+ }
69
+
70
+ const resolved = resolveSamplingKey(req, res)
71
+ if (!resolved) return SamplingDecision.SKIP
72
+
73
+ if (record && resolved.route === null) {
74
+ if (resolved.status === 404 || isBlocked(res)) return SamplingDecision.SKIP
75
+ return SamplingDecision.MISSING_ROUTE
76
+ }
77
+
78
+ if (sampledRequests.has(resolved.key)) return SamplingDecision.SKIP
79
+
80
+ if (asmStandaloneEnabled) {
81
+ keepTrace(rootSpan, ASM)
82
+ }
83
+
84
+ if (record) {
85
+ sampledRequests.set(resolved.key, undefined)
86
+ }
87
+
88
+ return SamplingDecision.SAMPLE
89
+ }
90
+
91
+ /**
92
+ * @param {import('http').IncomingMessage} req
93
+ * @param {import('http').ServerResponse} res
94
+ * @returns {boolean} Whether this request's endpoint is currently recorded in the TTL cache.
95
+ */
96
+ function wasSampled (req, res) {
97
+ const resolved = resolveSamplingKey(req, res)
98
+ return resolved !== null && sampledRequests.has(resolved.key)
99
+ }
100
+
101
+ function resolveSamplingKey (req, res) {
102
+ const method = req.method
103
+ const status = res.statusCode
104
+
105
+ if (!method || !status) {
106
+ log.warn('[ASM] Unsupported groupkey for API security')
107
+ return null
108
+ }
109
+
110
+ const context = web.getContext(req)
111
+ const route = getRouteOrEndpoint(context, status)
112
+
113
+ // route === null signals "no route information at all". An empty string is still a valid
114
+ // route (dd-trace-js represents the express root path '/' as an empty path segment).
115
+ return { method, status, route, key: method + (route ?? '') + status }
116
+ }
117
+
118
+ function getRouteOrEndpoint (context, statusCode) {
119
+ // The router plugin populates `context.paths` whenever the framework matched something.
120
+ // For express's root '/' route the matched path is normalized to '' (see datadog-plugin-router),
121
+ // so `paths.length > 0` is the signal that the framework provided route information — even when
122
+ // the joined string is empty. An empty `paths` array means no router involvement at all.
123
+ const paths = context?.paths
124
+ if (paths !== undefined && paths.length > 0) {
125
+ return paths.join('')
126
+ }
127
+
128
+ if (statusCode === 404) return null
129
+
130
+ const endpoint = context?.span?.context()?.getTag?.('http.endpoint')
131
+ if (endpoint) return endpoint
132
+
133
+ return null
134
+ }
135
+
136
+ function getSpanPriority (span) {
137
+ const spanContext = span.context?.()
138
+ return spanContext._sampling?.priority
139
+ }
140
+
141
+ module.exports = {
142
+ configure,
143
+ disable,
144
+ sampleRequest,
145
+ wasSampled,
146
+ SamplingDecision,
147
+ }