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.
- package/README.md +13 -8
- package/ci/init.js +25 -5
- package/ci/vitest-no-worker-init-setup.mjs +430 -0
- package/ext/tags.js +2 -0
- package/index.d.ts +79 -24
- package/initialize.mjs +5 -6
- package/loader-hook.mjs +154 -47
- package/package.json +20 -16
- package/packages/datadog-esbuild/index.js +26 -0
- package/packages/datadog-esbuild/src/utils.js +46 -3
- package/packages/datadog-instrumentations/src/connect.js +4 -3
- package/packages/datadog-instrumentations/src/fastify.js +4 -12
- package/packages/datadog-instrumentations/src/fs.js +8 -6
- package/packages/datadog-instrumentations/src/graphql.js +26 -484
- package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +81 -8
- package/packages/datadog-instrumentations/src/helpers/instrument.js +27 -2
- package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +65 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +6 -4
- package/packages/datadog-instrumentations/src/helpers/require-optional-peer.js +17 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql.js +201 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +18 -2
- package/packages/datadog-instrumentations/src/koa.js +3 -2
- package/packages/datadog-instrumentations/src/mariadb.js +4 -2
- package/packages/datadog-instrumentations/src/mocha/main.js +3 -3
- package/packages/datadog-instrumentations/src/mongoose.js +14 -2
- package/packages/datadog-instrumentations/src/next.js +10 -10
- package/packages/datadog-instrumentations/src/otel-sdk-trace.js +7 -6
- package/packages/datadog-instrumentations/src/playwright.js +1 -1
- package/packages/datadog-instrumentations/src/restify.js +4 -3
- package/packages/datadog-instrumentations/src/router.js +18 -5
- package/packages/datadog-instrumentations/src/selenium.js +1 -2
- package/packages/datadog-instrumentations/src/tedious.js +28 -0
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +1293 -0
- package/packages/datadog-instrumentations/src/vitest-main.js +1567 -0
- package/packages/datadog-instrumentations/src/vitest-util.js +249 -0
- package/packages/datadog-instrumentations/src/vitest-worker.js +753 -0
- package/packages/datadog-instrumentations/src/vitest.js +11 -1598
- package/packages/datadog-plugin-avsc/src/schema_iterator.js +2 -2
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/client.js +2 -2
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/context.js +18 -10
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +47 -11
- package/packages/datadog-plugin-aws-sdk/src/base.js +13 -9
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +43 -6
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +83 -91
- package/packages/datadog-plugin-cucumber/src/index.js +1 -1
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +19 -5
- package/packages/datadog-plugin-cypress/src/support.js +45 -1
- package/packages/datadog-plugin-express/src/code_origin.js +1 -1
- package/packages/datadog-plugin-fastify/src/code_origin.js +1 -1
- package/packages/datadog-plugin-graphql/src/execute.js +627 -11
- package/packages/datadog-plugin-graphql/src/index.js +20 -8
- package/packages/datadog-plugin-graphql/src/parse.js +24 -4
- package/packages/datadog-plugin-graphql/src/validate.js +16 -5
- package/packages/datadog-plugin-http/src/client.js +8 -3
- package/packages/datadog-plugin-http/src/index.js +6 -8
- package/packages/datadog-plugin-http2/src/client.js +5 -2
- package/packages/datadog-plugin-jest/src/index.js +1 -1
- package/packages/datadog-plugin-mocha/src/index.js +1 -1
- package/packages/datadog-plugin-mysql/src/index.js +7 -2
- package/packages/datadog-plugin-openai/src/services.js +2 -2
- package/packages/datadog-plugin-playwright/src/index.js +1 -1
- package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +2 -2
- package/packages/datadog-plugin-undici/src/index.js +5 -2
- package/packages/datadog-plugin-vitest/src/index.js +76 -64
- package/packages/datadog-shimmer/src/shimmer.js +37 -16
- package/packages/datadog-webpack/index.js +17 -1
- package/packages/datadog-webpack/src/optional-peer-loader.js +17 -0
- package/packages/dd-trace/src/aiguard/sdk.js +15 -3
- package/packages/dd-trace/src/appsec/api_security/index.js +55 -0
- package/packages/dd-trace/src/appsec/api_security/sampler.js +147 -0
- package/packages/dd-trace/src/appsec/channels.js +3 -1
- package/packages/dd-trace/src/appsec/downstream_requests.js +4 -4
- package/packages/dd-trace/src/appsec/graphql.js +5 -5
- package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +26 -19
- package/packages/dd-trace/src/appsec/iast/security-controls/index.js +2 -2
- package/packages/dd-trace/src/appsec/iast/telemetry/index.js +2 -1
- package/packages/dd-trace/src/appsec/index.js +20 -7
- package/packages/dd-trace/src/appsec/lambda.js +135 -0
- package/packages/dd-trace/src/appsec/reporter.js +50 -10
- package/packages/dd-trace/src/appsec/telemetry/api_security.js +34 -0
- package/packages/dd-trace/src/appsec/telemetry/index.js +27 -1
- package/packages/dd-trace/src/appsec/waf/index.js +16 -4
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +4 -4
- package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +21 -6
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +2 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +3 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +55 -13
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +2 -2
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +17 -0
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +47 -26
- package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +2 -2
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +64 -53
- package/packages/dd-trace/src/ci-visibility/requests/request.js +1 -17
- package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +3 -3
- package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +23 -5
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +128 -0
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +287 -0
- package/packages/dd-trace/src/config/defaults.js +25 -5
- package/packages/dd-trace/src/config/generated-config-types.d.ts +596 -58
- package/packages/dd-trace/src/config/helper.js +134 -33
- package/packages/dd-trace/src/config/index.js +39 -23
- package/packages/dd-trace/src/config/major-overrides.js +4 -2
- package/packages/dd-trace/src/config/parsers.js +19 -1
- package/packages/dd-trace/src/config/remote_config.js +1 -1
- package/packages/dd-trace/src/config/supported-configurations.json +109 -57
- package/packages/dd-trace/src/constants.js +7 -0
- package/packages/dd-trace/src/datastreams/processor.js +11 -3
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +47 -35
- package/packages/dd-trace/src/debugger/devtools_client/index.js +31 -66
- package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +83 -0
- package/packages/dd-trace/src/debugger/devtools_client/state.js +1 -0
- package/packages/dd-trace/src/debugger/index.js +5 -0
- package/packages/dd-trace/src/debugger/probe_sampler.js +112 -0
- package/packages/dd-trace/src/debugger/probe_sampler_constants.js +19 -0
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +77 -26
- package/packages/dd-trace/src/exporters/agent/index.js +1 -1
- package/packages/dd-trace/src/exporters/agentless/writer.js +4 -4
- package/packages/dd-trace/src/exporters/common/request.js +1 -20
- package/packages/dd-trace/src/exporters/common/url.js +34 -0
- package/packages/dd-trace/src/exporters/span-stats/writer.js +1 -3
- package/packages/dd-trace/src/guardrails/index.js +8 -5
- package/packages/dd-trace/src/index.js +6 -4
- package/packages/dd-trace/src/llmobs/constants/tags.js +6 -0
- package/packages/dd-trace/src/llmobs/index.js +17 -6
- package/packages/dd-trace/src/llmobs/plugins/base.js +4 -4
- package/packages/dd-trace/src/llmobs/plugins/genai/util.js +3 -3
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +5 -1
- package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
- package/packages/dd-trace/src/llmobs/sdk.js +8 -6
- package/packages/dd-trace/src/llmobs/span_processor.js +5 -1
- package/packages/dd-trace/src/llmobs/tagger.js +58 -3
- package/packages/dd-trace/src/llmobs/telemetry.js +1 -0
- package/packages/dd-trace/src/llmobs/writers/base.js +1 -1
- package/packages/dd-trace/src/log/index.js +18 -20
- package/packages/dd-trace/src/openfeature/flagging_provider.js +2 -7
- package/packages/dd-trace/src/opentelemetry/bridge-span-base.js +5 -4
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +54 -5
- package/packages/dd-trace/src/opentelemetry/metrics/instruments.js +2 -1
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +40 -0
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +169 -0
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +4 -3
- package/packages/dd-trace/src/opentelemetry/metrics/time.js +19 -0
- package/packages/dd-trace/src/opentelemetry/otlp/common.proto +2 -2
- package/packages/dd-trace/src/opentelemetry/otlp/logs.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/otlp/logs_service.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/otlp/metrics.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/otlp/metrics_service.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +44 -38
- package/packages/dd-trace/src/opentelemetry/span-helpers.js +29 -13
- package/packages/dd-trace/src/opentelemetry/span.js +14 -3
- package/packages/dd-trace/src/opentelemetry/span_context.js +1 -0
- package/packages/dd-trace/src/opentelemetry/trace/index.js +6 -1
- package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +3 -2
- package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +26 -13
- package/packages/dd-trace/src/opentelemetry/tracer_provider.js +1 -1
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +3 -3
- package/packages/dd-trace/src/opentracing/span_context.js +17 -0
- package/packages/dd-trace/src/opentracing/tracer.js +6 -1
- package/packages/dd-trace/src/plugin_manager.js +14 -8
- package/packages/dd-trace/src/plugins/ci_plugin.js +21 -4
- package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +276 -0
- package/packages/dd-trace/src/plugins/util/llm.js +6 -1
- package/packages/dd-trace/src/plugins/util/test.js +91 -9
- package/packages/dd-trace/src/plugins/util/url.js +68 -1
- package/packages/dd-trace/src/plugins/util/web.js +19 -36
- package/packages/dd-trace/src/priority_sampler.js +1 -13
- package/packages/dd-trace/src/profiling/config.js +129 -198
- package/packages/dd-trace/src/profiling/constants.js +4 -7
- package/packages/dd-trace/src/profiling/exporter_cli.js +10 -10
- package/packages/dd-trace/src/profiling/exporters/agent.js +23 -16
- package/packages/dd-trace/src/profiling/exporters/event_serializer.js +45 -14
- package/packages/dd-trace/src/profiling/exporters/file.js +16 -5
- package/packages/dd-trace/src/profiling/oom.js +71 -0
- package/packages/dd-trace/src/profiling/profiler.js +35 -23
- package/packages/dd-trace/src/profiling/profilers/events.js +14 -7
- package/packages/dd-trace/src/profiling/profilers/space.js +27 -18
- package/packages/dd-trace/src/profiling/profilers/wall.js +35 -25
- package/packages/dd-trace/src/proxy.js +8 -8
- package/packages/dd-trace/src/ritm.js +4 -2
- package/packages/dd-trace/src/serverless.js +1 -2
- package/packages/dd-trace/src/service-naming/schemas/util.js +36 -5
- package/packages/dd-trace/src/service-naming/schemas/v1/web.js +5 -3
- package/packages/dd-trace/src/span_format.js +2 -1
- package/packages/dd-trace/src/span_processor.js +11 -6
- package/packages/dd-trace/src/span_stats.js +96 -78
- package/packages/dd-trace/src/startup-log.js +2 -1
- package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
- package/packages/dd-trace/src/telemetry/endpoints.js +6 -4
- package/packages/dd-trace/src/telemetry/index.js +2 -2
- package/packages/dd-trace/src/telemetry/logs/index.js +1 -1
- package/packages/dd-trace/src/telemetry/send-data.js +20 -18
- package/packages/dd-trace/src/telemetry/session-propagation.js +1 -1
- package/packages/dd-trace/src/telemetry/telemetry.js +9 -9
- package/packages/dd-trace/src/tracer.js +15 -1
- package/packages/dd-trace/src/util.js +14 -0
- package/register.js +58 -1
- package/vendor/dist/@apm-js-collab/code-transformer/index.js +1 -1
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/pprof-format/index.js +1 -1
- package/vendor/dist/protobufjs/index.js +1 -1
- package/vendor/dist/protobufjs/minimal/index.js +1 -1
- package/vendor/dist/shell-quote/index.js +1 -1
- package/packages/datadog-plugin-graphql/src/resolve.js +0 -170
- 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
|
-
[](https://www.npmjs.com/package/dd-trace)
|
|
4
4
|
[](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) |  | `>= v12` | NO | NO | **EOL** | 2022-01-28 | 2023-08-15 |
|
|
29
29
|
| [`v3`](https://github.com/DataDog/dd-trace-js/tree/v3.x) |  | `>= v14` | NO | YES | **EOL** | 2022-08-15 | 2024-05-15 |
|
|
30
30
|
| [`v4`](https://github.com/DataDog/dd-trace-js/tree/v4.x) |  | `>= v16` | YES | YES | **EOL** | 2023-05-12 | 2025-01-11 |
|
|
31
|
-
| [`v5`](https://github.com/DataDog/dd-trace-js/tree/v5.x) | 
|
|
31
|
+
| [`v5`](https://github.com/DataDog/dd-trace-js/tree/v5.x) |  | `>= v18` | YES | YES | **Maintenance** | 2024-01-11 | Unknown |
|
|
32
|
+
| [`v6`](https://github.com/DataDog/dd-trace-js/tree/v6.x) |  | `>= 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
|
|
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 `
|
|
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
|
|
46
|
-
|
|
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
|
-
|
|
47
|
-
|
|
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
|
-
|
|
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
|
+
}
|