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
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # `dd-trace`: Node.js APM Tracer Library
2
2
 
3
- [![npm v5](https://img.shields.io/npm/v/dd-trace/latest?color=blue&label=dd-trace%40v5&logo=npm)](https://www.npmjs.com/package/dd-trace)
3
+ [![npm v6](https://img.shields.io/npm/v/dd-trace/latest?color=blue&label=dd-trace%40v6&logo=npm)](https://www.npmjs.com/package/dd-trace)
4
4
  [![codecov](https://codecov.io/gh/DataDog/dd-trace-js/branch/master/graph/badge.svg)](https://codecov.io/gh/DataDog/dd-trace-js)
5
5
 
6
6
  <img align="right" src="https://user-images.githubusercontent.com/551402/208212084-1d0c07e2-4135-4c61-b2da-8f2fddbc66ed.png" alt="Bits the dog JavaScript" width="200px"/>
@@ -28,30 +28,35 @@ Most of the documentation for `dd-trace` is available on these webpages:
28
28
  | [`v2`](https://github.com/DataDog/dd-trace-js/tree/v2.x) | ![npm v2](https://img.shields.io/npm/v/dd-trace/latest-node12?color=white&label=%20&style=flat-square) | `>= v12` | NO | NO | **EOL** | 2022-01-28 | 2023-08-15 |
29
29
  | [`v3`](https://github.com/DataDog/dd-trace-js/tree/v3.x) | ![npm v3](https://img.shields.io/npm/v/dd-trace/latest-node14?color=white&label=%20&style=flat-square) | `>= v14` | NO | YES | **EOL** | 2022-08-15 | 2024-05-15 |
30
30
  | [`v4`](https://github.com/DataDog/dd-trace-js/tree/v4.x) | ![npm v4](https://img.shields.io/npm/v/dd-trace/latest-node16?color=white&label=%20&style=flat-square) | `>= v16` | YES | YES | **EOL** | 2023-05-12 | 2025-01-11 |
31
- | [`v5`](https://github.com/DataDog/dd-trace-js/tree/v5.x) | ![npm v5](https://img.shields.io/npm/v/dd-trace/latest?color=white&label=%20&style=flat-square) | `>= v18` | YES | YES | **Current** | 2024-01-11 | Unknown |
31
+ | [`v5`](https://github.com/DataDog/dd-trace-js/tree/v5.x) | ![npm v5](https://img.shields.io/npm/v/dd-trace/latest-node18?color=white&label=%20&style=flat-square) | `>= v18` | YES | YES | **Maintenance** | 2024-01-11 | Unknown |
32
+ | [`v6`](https://github.com/DataDog/dd-trace-js/tree/v6.x) | ![npm v6](https://img.shields.io/npm/v/dd-trace/latest?color=white&label=%20&style=flat-square) | `>= v22` | YES | YES | **Current** | 2026-07-02 | Unknown |
32
33
 
33
34
  * EOL = End-of-life
34
35
  * SSI = Single-Step Install
35
36
 
36
- We currently maintain one release line, namely `v5`.
37
+ We currently maintain two release lines: `v6` (current) and `v5` (maintenance).
37
38
 
38
- For any new projects it is recommended to use the `v5` release line:
39
+ For any new projects it is recommended to use the `v6` release line:
39
40
 
40
41
  ```sh
41
42
  $ npm install dd-trace
42
43
  $ yarn add dd-trace
43
44
  ```
44
45
 
45
- Existing projects that need to use EOL versions of Node.js may continue to use these older release lines.
46
- This is done by specifying the version when installing the package.
46
+ Existing projects on `v5` may continue to use it during its maintenance window by pinning the major version:
47
+
48
+ ```sh
49
+ $ npm install dd-trace@5
50
+ $ yarn add dd-trace@5
51
+ ```
52
+
53
+ Older EOL release lines are no longer maintained, but can still be pinned if needed:
47
54
 
48
55
  ```sh
49
56
  $ npm install dd-trace@4 # or whatever version you need
50
57
  $ yarn add dd-trace@4 # or whatever version you need
51
58
  ```
52
59
 
53
- Note, however, that the end-of-life release lines are no longer maintained and will not receive updates.
54
-
55
60
  Any backwards-breaking functionality that is introduced into the library will result in an increase of the major version of the library and therefore a new release line.
56
61
  Such releases are kept to a minimum to reduce the pain of upgrading the library.
57
62
 
package/ci/init.js CHANGED
@@ -1,14 +1,14 @@
1
1
  'use strict'
2
2
 
3
3
  /* eslint-disable no-console */
4
- const tracer = require('../packages/dd-trace')
5
- const { isTrue, isFalse } = require('../packages/dd-trace/src/util')
6
4
  const log = require('../packages/dd-trace/src/log')
7
5
  const { getEnvironmentVariable, getValueFromEnvSources } = require('../packages/dd-trace/src/config/helper')
6
+ const { isTrue } = require('../packages/dd-trace/src/util')
8
7
 
9
8
  const PACKAGE_MANAGERS = ['npm', 'yarn', 'pnpm']
10
9
  const DEFAULT_FLUSH_INTERVAL = 5000
11
10
  const JEST_FLUSH_INTERVAL = 0
11
+ const VITEST_NO_WORKER_INIT_ACTIVE_ENV = 'DD_TEST_OPT_VITEST_NO_WORKER_INIT_ACTIVE'
12
12
  const EXPORTER_MAP = {
13
13
  jest: 'jest_worker',
14
14
  cucumber: 'cucumber_worker',
@@ -43,8 +43,10 @@ const baseOptions = {
43
43
  flushInterval: isJestWorker ? JEST_FLUSH_INTERVAL : DEFAULT_FLUSH_INTERVAL,
44
44
  }
45
45
 
46
- let shouldInit = !isFalse(getValueFromEnvSources('DD_CIVISIBILITY_ENABLED'))
47
- const isAgentlessEnabled = isTrue(getValueFromEnvSources('DD_CIVISIBILITY_AGENTLESS_ENABLED'))
46
+ // skipDefault: CI visibility stays on unless DD_CIVISIBILITY_ENABLED is explicitly false; the
47
+ // registered default (false) would otherwise turn it off whenever the variable is unset.
48
+ let shouldInit = getValueFromEnvSources('DD_CIVISIBILITY_ENABLED', true) !== false
49
+ const isAgentlessEnabled = getValueFromEnvSources('DD_CIVISIBILITY_AGENTLESS_ENABLED')
48
50
 
49
51
  if (!isTestWorker && isPackageManager()) {
50
52
  log.debug('dd-trace is not initialized in a package manager.')
@@ -77,10 +79,28 @@ if (isTestWorker) {
77
79
  }
78
80
  }
79
81
 
80
- if (shouldInit) {
82
+ const skipVitestWorkerInit = shouldSkipVitestWorkerInit()
83
+ const tracer = skipVitestWorkerInit
84
+ ? {
85
+ init () {},
86
+ use () {},
87
+ }
88
+ : require('../packages/dd-trace')
89
+
90
+ if (shouldInit && !skipVitestWorkerInit) {
81
91
  tracer.init(baseOptions)
82
92
  tracer.use('fs', false)
83
93
  tracer.use('child_process', false)
84
94
  }
85
95
 
86
96
  module.exports = tracer
97
+
98
+ function shouldSkipVitestWorkerInit () {
99
+ return getValueFromEnvSources('DD_VITEST_WORKER') &&
100
+ isVitestNoWorkerInitActive()
101
+ }
102
+
103
+ function isVitestNoWorkerInitActive () {
104
+ // eslint-disable-next-line eslint-rules/eslint-process-env
105
+ return isTrue(process.env[VITEST_NO_WORKER_INIT_ACTIVE_ENV])
106
+ }
@@ -0,0 +1,430 @@
1
+ import { realpathSync } from 'node:fs'
2
+ import { relative } from 'node:path'
3
+ import { performance } from 'node:perf_hooks'
4
+
5
+ import { afterEach, beforeAll, beforeEach } from 'vitest'
6
+
7
+ // Instrumentation-less worker setup for DD_EXPERIMENTAL_TEST_OPT_VITEST_NO_WORKER_INIT.
8
+ // It applies Test Optimization execution changes without initializing dd-trace in Vitest workers.
9
+ const VITEST_NO_WORKER_INIT_ACTIVE_ENV = 'DD_TEST_OPT_VITEST_NO_WORKER_INIT_ACTIVE'
10
+ const isNoWorkerInitActive = getIsNoWorkerInitActive()
11
+ const providedContext = isNoWorkerInitActive ? getProvidedContext() : {}
12
+ const attemptToFixTests = providedContext.attemptToFixTests || {}
13
+ const attemptToFixRetries = providedContext.attemptToFixRetries || 0
14
+ const disabledTests = providedContext.disabledTests || {}
15
+ const earlyFlakeDetectionRetries = providedContext.earlyFlakeDetectionRetries || 0
16
+ const earlyFlakeDetectionRetryThresholds = Array.isArray(providedContext.earlyFlakeDetectionRetryThresholds)
17
+ ? providedContext.earlyFlakeDetectionRetryThresholds
18
+ : []
19
+ const earlyFlakeDetectionSlowRetries = providedContext.earlyFlakeDetectionSlowRetries || {}
20
+ const hasEarlyFlakeDetectionSlowRetries = Object.keys(earlyFlakeDetectionSlowRetries).length > 0
21
+ const isEarlyFlakeDetectionEnabled = providedContext.isEarlyFlakeDetectionEnabled === true
22
+ const knownTests = providedContext.knownTests || {}
23
+ const modifiedFiles = providedContext.modifiedFiles || {}
24
+ const quarantinedTests = providedContext.quarantinedTests || {}
25
+ const repositoryRoot = realpath(providedContext.repositoryRoot || process.cwd())
26
+ let setVitestTaskFn
27
+ if (isNoWorkerInitActive) {
28
+ try {
29
+ // Vitest does not expose setFn from the public setup API; keep this optional for strict installers.
30
+ const vitestRunner = await import('@vitest/runner')
31
+ setVitestTaskFn = vitestRunner.setFn
32
+ } catch {}
33
+ }
34
+ const earlyFlakeDetectionRetriesByTask = new WeakMap()
35
+ const earlyFlakeDetectionSkippedResults = new WeakMap()
36
+ const earlyFlakeDetectionStartByTask = new WeakMap()
37
+ const nextAttemptIndexByTask = new WeakMap()
38
+ const realpaths = new Map()
39
+
40
+ if (isNoWorkerInitActive) {
41
+ // eslint-disable-next-line no-empty-pattern
42
+ beforeAll(function ({}, suite) {
43
+ suite ||= arguments[0]
44
+ applyExecutionChanges(suite)
45
+ })
46
+
47
+ beforeEach(function ({ onTestFinished, task, skip }) {
48
+ const testSuite = getTestSuite(task)
49
+ const testName = getTestName(task)
50
+ const isAttemptToFixTest = attemptToFixTests[testSuite]?.[testName]
51
+ const isEarlyFlakeDetectionTestAttempt = isEarlyFlakeDetectionTest(testSuite, testName)
52
+ const isQuarantinedTest = quarantinedTests[testSuite]?.[testName] && !isAttemptToFixTest
53
+ const attemptIndex = getNextAttemptIndex(task)
54
+ if (attemptIndex > 0) {
55
+ recordTestOptimizationStatus(task, attemptIndex - 1)
56
+ }
57
+
58
+ if (disabledTests[testSuite]?.[testName] && !isAttemptToFixTest) {
59
+ skip('Skipped by Datadog Test Optimization')
60
+ } else if (isAttemptToFixTest && attemptIndex > 0) {
61
+ task.result.state = 'run'
62
+ } else if (isEarlyFlakeDetectionTestAttempt) {
63
+ const isSkippedRepeat = prepareEarlyFlakeDetectionAttempt(task, attemptIndex)
64
+ if (!isSkippedRepeat && attemptIndex > 0) {
65
+ task.result.state = 'run'
66
+ }
67
+ }
68
+
69
+ if (isAttemptToFixTest || isEarlyFlakeDetectionTestAttempt || isQuarantinedTest) {
70
+ onTestFinished(() => {
71
+ if (attemptIndex === getFinalAttemptIndex(task)) {
72
+ if (isAttemptToFixTest || isEarlyFlakeDetectionTestAttempt) {
73
+ recordTestOptimizationStatus(task, attemptIndex, true)
74
+ }
75
+ switchQuarantinedFinalFailure(task, attemptIndex)
76
+ }
77
+ })
78
+ }
79
+ })
80
+
81
+ afterEach(function ({ task }) {
82
+ const attemptIndex = task.meta.__ddTestOptCurrentAttemptIndex
83
+ if (restoreEarlyFlakeDetectionSkippedResult(task)) {
84
+ return
85
+ }
86
+ if (attemptIndex === getFinalAttemptIndex(task)) {
87
+ recordTestOptimizationStatus(task, attemptIndex)
88
+ }
89
+ switchQuarantinedFinalFailure(task, attemptIndex)
90
+ })
91
+ }
92
+
93
+ function applyExecutionChanges (suite) {
94
+ for (const task of suite?.tasks || []) {
95
+ if (task.type === 'suite') {
96
+ applyExecutionChanges(task)
97
+ continue
98
+ }
99
+
100
+ const testSuite = getTestSuite(task)
101
+ const testName = getTestName(task)
102
+ if (attemptToFixTests[testSuite]?.[testName]) {
103
+ task.retry = 0
104
+ task.repeats = attemptToFixRetries
105
+ task.meta.__ddTestOptAtfRetries = attemptToFixRetries
106
+ } else if (disabledTests[testSuite]?.[testName]) {
107
+ task.mode = 'skip'
108
+ } else if (isEarlyFlakeDetectionTest(testSuite, testName)) {
109
+ task.retry = 0
110
+ task.repeats = earlyFlakeDetectionRetries
111
+ task.meta.__ddTestOptEfdRetries = earlyFlakeDetectionRetries
112
+ }
113
+ }
114
+ }
115
+
116
+ function getNextAttemptIndex (task) {
117
+ const attemptIndex = nextAttemptIndexByTask.get(task) || 0
118
+ nextAttemptIndexByTask.set(task, attemptIndex + 1)
119
+ task.meta.__ddTestOptCurrentAttemptIndex = attemptIndex
120
+ return attemptIndex
121
+ }
122
+
123
+ function recordTestOptimizationStatus (task, attemptIndex = task.result?.repeatCount || 0, onlyIfNewErrors = false) {
124
+ const testSuite = getTestSuite(task)
125
+ const testName = getTestName(task)
126
+
127
+ if (attemptToFixTests[testSuite]?.[testName]) {
128
+ recordAttemptToFixStatus(task, attemptIndex, onlyIfNewErrors)
129
+ } else if (isEarlyFlakeDetectionTest(testSuite, testName)) {
130
+ recordEarlyFlakeDetectionStatus(task, attemptIndex, onlyIfNewErrors)
131
+ } else if (task.repeats > 0) {
132
+ recordManualRepeatStatus(task, attemptIndex)
133
+ }
134
+ }
135
+
136
+ function recordAttemptToFixStatus (task, attemptIndex, onlyIfNewErrors) {
137
+ if (onlyIfNewErrors && !hasNewErrors(task.meta.__ddTestOptAtfErrorCounts, attemptIndex, task)) {
138
+ return
139
+ }
140
+
141
+ task.meta.__ddTestOptAtfStatuses ||= []
142
+ task.meta.__ddTestOptAtfErrorCounts ||= []
143
+ task.meta.__ddTestOptAtfStatuses[attemptIndex] = getAttemptStatus(
144
+ task,
145
+ task.meta.__ddTestOptAtfErrorCounts,
146
+ attemptIndex
147
+ )
148
+ task.meta.__ddTestOptAtfErrorCounts[attemptIndex] = task.result?.errors?.length || 0
149
+
150
+ if (
151
+ attemptIndex === getAttemptToFixRetryCount(task) &&
152
+ task.meta.__ddTestOptAtfStatuses.includes('fail') &&
153
+ task.result?.state === 'pass'
154
+ ) {
155
+ task.result.state = 'fail'
156
+ }
157
+ }
158
+
159
+ function recordEarlyFlakeDetectionStatus (task, attemptIndex, onlyIfNewErrors) {
160
+ if (restoreEarlyFlakeDetectionSkippedResult(task)) {
161
+ return
162
+ }
163
+
164
+ const retryCount = earlyFlakeDetectionRetriesByTask.get(task)
165
+ if (retryCount !== undefined && attemptIndex > retryCount) {
166
+ return
167
+ }
168
+
169
+ if (onlyIfNewErrors && !hasNewErrors(task.meta.__ddTestOptEfdErrorCounts, attemptIndex, task)) {
170
+ return
171
+ }
172
+
173
+ if (!earlyFlakeDetectionRetriesByTask.has(task)) {
174
+ const retryCount = getEarlyFlakeDetectionRetryCount(task)
175
+ earlyFlakeDetectionRetriesByTask.set(task, retryCount)
176
+ task.repeats = retryCount
177
+ task.meta.__ddTestOptEfdRetries = retryCount
178
+ if (retryCount === 0 && hasEarlyFlakeDetectionSlowRetries) {
179
+ task.meta.__ddTestOptEfdAbortReason = 'slow'
180
+ }
181
+ }
182
+
183
+ task.meta.__ddTestOptEfdStatuses ||= []
184
+ task.meta.__ddTestOptEfdErrorCounts ||= []
185
+ task.meta.__ddTestOptEfdStatuses[attemptIndex] = getAttemptStatus(
186
+ task,
187
+ task.meta.__ddTestOptEfdErrorCounts,
188
+ attemptIndex
189
+ )
190
+ task.meta.__ddTestOptEfdErrorCounts[attemptIndex] = task.result?.errors?.length || 0
191
+
192
+ if (attemptIndex === getEarlyFlakeDetectionRetryCountForTask(task) &&
193
+ task.meta.__ddTestOptEfdStatuses.includes('pass')) {
194
+ task.result.state = 'pass'
195
+ }
196
+ }
197
+
198
+ function recordManualRepeatStatus (task, attemptIndex) {
199
+ task.meta.__ddTestOptRepeatStatuses ||= []
200
+ task.meta.__ddTestOptRepeatErrorCounts ||= []
201
+ task.meta.__ddTestOptRepeatStatuses[attemptIndex] = getManualRepeatStatus(
202
+ task,
203
+ task.meta.__ddTestOptRepeatErrorCounts,
204
+ attemptIndex
205
+ )
206
+ task.meta.__ddTestOptRepeatErrorCounts[attemptIndex] = task.result?.errors?.length || 0
207
+ }
208
+
209
+ function restoreEarlyFlakeDetectionSkippedResult (task) {
210
+ if (!task.meta.__ddTestOptEfdSkipCurrentAttempt) {
211
+ return false
212
+ }
213
+
214
+ delete task.meta.__ddTestOptEfdSkipCurrentAttempt
215
+ const skippedResult = earlyFlakeDetectionSkippedResults.get(task)
216
+ if (skippedResult) {
217
+ task.result = skippedResult
218
+ earlyFlakeDetectionSkippedResults.delete(task)
219
+ }
220
+ return true
221
+ }
222
+
223
+ function hasNewErrors (errorCounts, attemptIndex, task) {
224
+ const recordedErrorCount = errorCounts?.[attemptIndex]
225
+ const previousErrorCount = recordedErrorCount ?? getPreviousErrorCount(errorCounts, attemptIndex)
226
+ return (task.result?.errors?.length || 0) > previousErrorCount
227
+ }
228
+
229
+ function getFinalAttemptIndex (task) {
230
+ const testSuite = getTestSuite(task)
231
+ const testName = getTestName(task)
232
+
233
+ if (attemptToFixTests[testSuite]?.[testName]) {
234
+ return getAttemptToFixRetryCount(task)
235
+ }
236
+ if (isEarlyFlakeDetectionTest(testSuite, testName)) {
237
+ return getEarlyFlakeDetectionRetryCountForTask(task)
238
+ }
239
+ return task.repeats
240
+ }
241
+
242
+ function switchQuarantinedFinalFailure (task, attemptIndex) {
243
+ const testSuite = getTestSuite(task)
244
+ const testName = getTestName(task)
245
+ if (
246
+ !quarantinedTests[testSuite]?.[testName] ||
247
+ attemptToFixTests[testSuite]?.[testName] ||
248
+ task.result?.state !== 'fail'
249
+ ) {
250
+ return
251
+ }
252
+
253
+ const retryCount = task.result?.retryCount || 0
254
+ const retryLimit = task.retry || 0
255
+ if (retryCount < retryLimit || attemptIndex < getFinalAttemptIndex(task)) {
256
+ return
257
+ }
258
+
259
+ task.meta.__ddTestOptQuarantinedFailed = true
260
+ task.result.state = 'pass'
261
+ }
262
+
263
+ function getAttemptToFixRetryCount (task) {
264
+ return task.meta.__ddTestOptAtfRetries ?? task.repeats
265
+ }
266
+
267
+ function getEarlyFlakeDetectionRetryCountForTask (task) {
268
+ return earlyFlakeDetectionRetriesByTask.get(task) ?? task.meta.__ddTestOptEfdRetries ?? task.repeats
269
+ }
270
+
271
+ function getAttemptStatus (task, errorCounts, repeatCount) {
272
+ const errorCount = task.result?.errors?.length || 0
273
+ if (errorCount > getPreviousErrorCount(errorCounts, repeatCount)) {
274
+ return 'fail'
275
+ }
276
+ return task.result?.state === 'fail' ? 'fail' : 'pass'
277
+ }
278
+
279
+ function getManualRepeatStatus (task, errorCounts, repeatCount) {
280
+ const errorCount = task.result?.errors?.length || 0
281
+ return errorCount > getPreviousErrorCount(errorCounts, repeatCount) ? 'fail' : 'pass'
282
+ }
283
+
284
+ function getPreviousErrorCount (errorCounts, repeatCount) {
285
+ for (let index = repeatCount - 1; index >= 0; index--) {
286
+ if (errorCounts[index] !== undefined) {
287
+ return errorCounts[index]
288
+ }
289
+ }
290
+ return 0
291
+ }
292
+
293
+ function prepareEarlyFlakeDetectionAttempt (task, attemptIndex) {
294
+ if (attemptIndex === 0) {
295
+ earlyFlakeDetectionStartByTask.set(task, performance.now())
296
+ return false
297
+ }
298
+
299
+ let retryCount = earlyFlakeDetectionRetriesByTask.get(task)
300
+ if (retryCount === undefined) {
301
+ retryCount = getEarlyFlakeDetectionRetryCount(task)
302
+ earlyFlakeDetectionRetriesByTask.set(task, retryCount)
303
+ task.repeats = retryCount
304
+ task.meta.__ddTestOptEfdRetries = retryCount
305
+ if (retryCount === 0 && hasEarlyFlakeDetectionSlowRetries) {
306
+ task.meta.__ddTestOptEfdAbortReason = 'slow'
307
+ }
308
+ }
309
+
310
+ if (attemptIndex <= retryCount) {
311
+ earlyFlakeDetectionStartByTask.set(task, performance.now())
312
+ return false
313
+ }
314
+
315
+ if (!canReplaceVitestTaskFn()) {
316
+ earlyFlakeDetectionStartByTask.set(task, performance.now())
317
+ return false
318
+ }
319
+
320
+ if (task.result) {
321
+ earlyFlakeDetectionSkippedResults.set(task, {
322
+ ...task.result,
323
+ errors: task.result.errors?.slice(),
324
+ })
325
+ }
326
+ task.meta.__ddTestOptEfdSkipCurrentAttempt = true
327
+ replaceVitestTaskFn(task, noopTest)
328
+ return true
329
+ }
330
+
331
+ function getEarlyFlakeDetectionRetryCount (task) {
332
+ if (!hasEarlyFlakeDetectionSlowRetries) {
333
+ return earlyFlakeDetectionRetries
334
+ }
335
+
336
+ const executionStart = earlyFlakeDetectionStartByTask.get(task)
337
+ const duration = executionStart === undefined ? task.result?.duration ?? 0 : performance.now() - executionStart
338
+ for (const { key, limitMs } of earlyFlakeDetectionRetryThresholds) {
339
+ if (duration < limitMs) {
340
+ return earlyFlakeDetectionSlowRetries[key] ?? 0
341
+ }
342
+ }
343
+ return 0
344
+ }
345
+
346
+ function noopTest () {}
347
+
348
+ /**
349
+ * Returns whether Vitest's private task function setter is available.
350
+ *
351
+ * @returns {boolean}
352
+ */
353
+ function canReplaceVitestTaskFn () {
354
+ return typeof setVitestTaskFn === 'function'
355
+ }
356
+
357
+ /**
358
+ * Replaces the function Vitest runs for a task.
359
+ *
360
+ * @param {object} task
361
+ * @param {(...args: unknown[]) => unknown} testFn
362
+ */
363
+ function replaceVitestTaskFn (task, testFn) {
364
+ setVitestTaskFn(task, testFn)
365
+ }
366
+
367
+ function isEarlyFlakeDetectionTest (testSuite, testName) {
368
+ if (!isEarlyFlakeDetectionEnabled || earlyFlakeDetectionRetries <= 0) return false
369
+ if (isModifiedTest(testSuite)) return true
370
+ const testsForSuite = knownTests[testSuite] || []
371
+ return !testsForSuite.includes(testName)
372
+ }
373
+
374
+ function isModifiedTest (testSuite) {
375
+ return modifiedFiles[testSuite]?.length > 0
376
+ }
377
+
378
+ function getTestSuite (task) {
379
+ let filepath = realpaths.get(task.file.filepath)
380
+ if (filepath === undefined) {
381
+ filepath = realpath(task.file.filepath)
382
+ realpaths.set(task.file.filepath, filepath)
383
+ }
384
+
385
+ return normalizePath(relative(repositoryRoot, filepath))
386
+ }
387
+
388
+ function realpath (filepath) {
389
+ try {
390
+ return realpathSync(filepath)
391
+ } catch {
392
+ return filepath
393
+ }
394
+ }
395
+
396
+ function getTestName (task) {
397
+ let testName = task.name
398
+ let currentTask = task.suite
399
+
400
+ while (currentTask) {
401
+ if (currentTask.name) {
402
+ testName = `${currentTask.name} ${testName}`
403
+ }
404
+ currentTask = currentTask.suite
405
+ }
406
+
407
+ return testName
408
+ }
409
+
410
+ function normalizePath (filepath) {
411
+ return filepath.replaceAll('\\', '/')
412
+ }
413
+
414
+ function getIsNoWorkerInitActive () {
415
+ try {
416
+ // eslint-disable-next-line eslint-rules/eslint-process-env
417
+ const value = process.env[VITEST_NO_WORKER_INIT_ACTIVE_ENV]
418
+ return value === '1' || value === 'true'
419
+ } catch {
420
+ return false
421
+ }
422
+ }
423
+
424
+ function getProvidedContext () {
425
+ try {
426
+ return globalThis.__vitest_worker__.providedContext._ddVitestWorkerSetup || {}
427
+ } catch {
428
+ return {}
429
+ }
430
+ }
package/ext/tags.js CHANGED
@@ -27,6 +27,8 @@ const tags = {
27
27
  HTTP_CLIENT_IP: 'http.client_ip',
28
28
  NETWORK_CLIENT_IP: 'network.client.ip',
29
29
 
30
+ GRPC_STATUS_CODE: 'grpc.status.code',
31
+
30
32
  // Messaging
31
33
 
32
34
  // DSM Specific