dd-trace 6.8.0 → 6.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ci/diagnose.js +27 -25
- package/ci/init.js +6 -6
- package/ci/runbook.md +114 -20
- package/ci/test-optimization-validation/approval-artifacts.js +36 -3
- package/ci/test-optimization-validation/approval.js +78 -22
- package/ci/test-optimization-validation/blocker-category.js +24 -0
- package/ci/test-optimization-validation/ci-discovery.js +23 -17
- package/ci/test-optimization-validation/ci-package-scripts.js +34 -6
- package/ci/test-optimization-validation/ci-remediation.js +1 -1
- package/ci/test-optimization-validation/cli.js +280 -52
- package/ci/test-optimization-validation/command-blocker.js +159 -20
- package/ci/test-optimization-validation/command-output-policy.js +9 -3
- package/ci/test-optimization-validation/command-runner.js +9 -7
- package/ci/test-optimization-validation/environment.js +4 -2
- package/ci/test-optimization-validation/executable.js +15 -13
- package/ci/test-optimization-validation/execution-lock.js +70 -0
- package/ci/test-optimization-validation/framework-adapters/cucumber-validation.json +3 -0
- package/ci/test-optimization-validation/framework-adapters/cucumber.js +657 -5
- package/ci/test-optimization-validation/framework-adapters/vitest.js +517 -0
- package/ci/test-optimization-validation/generated-files.js +38 -28
- package/ci/test-optimization-validation/generated-test-contract.js +8 -4
- package/ci/test-optimization-validation/generated-verifier.js +24 -17
- package/ci/test-optimization-validation/literal-glob.js +52 -0
- package/ci/test-optimization-validation/manifest-loader.js +3 -0
- package/ci/test-optimization-validation/manifest-scaffold.js +300 -50
- package/ci/test-optimization-validation/manifest-schema.js +80 -17
- package/ci/test-optimization-validation/offline-fixtures.js +13 -4
- package/ci/test-optimization-validation/package-check.js +94 -0
- package/ci/test-optimization-validation/plan-writer.js +183 -10
- package/ci/test-optimization-validation/preflight-runner.js +110 -31
- package/ci/test-optimization-validation/project-build-artifact.js +31 -0
- package/ci/test-optimization-validation/redaction.js +18 -27
- package/ci/test-optimization-validation/report-writer.js +182 -48
- package/ci/test-optimization-validation/result-semantics.js +20 -3
- package/ci/test-optimization-validation/runner-command.js +40 -20
- package/ci/test-optimization-validation/runner-contract.js +79 -43
- package/ci/test-optimization-validation/scenarios/basic-reporting.js +59 -5
- package/ci/test-optimization-validation/scenarios/ci-wiring.js +141 -35
- package/ci/test-optimization-validation/scenarios/helpers.js +30 -6
- package/ci/test-optimization-validation/static-diagnosis.js +10 -3
- package/ci/test-optimization-validation/test-output.js +2 -1
- package/ci/test-optimization-validation/validation-blocker.js +21 -0
- package/ci/vitest-no-worker-init-setup.mjs +140 -42
- package/index.d.ts +140 -1
- package/init.js +1 -17
- package/initialize.mjs +11 -0
- package/loader-hook.mjs +6 -4
- package/package.json +8 -5
- package/packages/datadog-core/src/utils/src/parse-tags.js +0 -1
- package/packages/datadog-esbuild/src/utils.js +16 -1
- package/packages/datadog-instrumentations/src/ai.js +61 -34
- package/packages/datadog-instrumentations/src/bluebird.js +6 -6
- package/packages/datadog-instrumentations/src/claude-agent-sdk.js +81 -5
- package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +111 -6
- package/packages/datadog-instrumentations/src/cucumber.js +124 -47
- package/packages/datadog-instrumentations/src/dns.js +21 -2
- package/packages/datadog-instrumentations/src/electron.js +1 -0
- package/packages/datadog-instrumentations/src/fastify.js +3 -1
- package/packages/datadog-instrumentations/src/grpc/client.js +4 -2
- package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +3 -1
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +629 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +34 -9
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +199 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +20 -2
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.js +32 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +62 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +83 -1
- package/packages/datadog-instrumentations/src/helpers/router-helper.js +37 -6
- package/packages/datadog-instrumentations/src/jest/coverage-backfill.js +1 -1
- package/packages/datadog-instrumentations/src/jest.js +980 -234
- package/packages/datadog-instrumentations/src/mocha/main.js +108 -10
- package/packages/datadog-instrumentations/src/mocha/utils.js +66 -50
- package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +25 -0
- package/packages/datadog-instrumentations/src/mocha/worker.js +12 -18
- package/packages/datadog-instrumentations/src/mysql.js +46 -16
- package/packages/datadog-instrumentations/src/mysql2.js +119 -18
- package/packages/datadog-instrumentations/src/next.js +155 -23
- package/packages/datadog-instrumentations/src/openai.js +14 -0
- package/packages/datadog-instrumentations/src/path-to-regexp.js +67 -1
- package/packages/datadog-instrumentations/src/pg.js +209 -11
- package/packages/datadog-instrumentations/src/playwright.js +173 -79
- package/packages/datadog-instrumentations/src/promise-js.js +4 -4
- package/packages/datadog-instrumentations/src/promise.js +3 -3
- package/packages/datadog-instrumentations/src/router.js +195 -19
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +85 -13
- package/packages/datadog-instrumentations/src/vitest-main.js +528 -72
- package/packages/datadog-instrumentations/src/vitest-util.js +106 -12
- package/packages/datadog-instrumentations/src/vitest-worker.js +382 -40
- package/packages/datadog-instrumentations/src/webdriverio.js +212 -22
- package/packages/datadog-instrumentations/src/when.js +3 -3
- package/packages/datadog-instrumentations/src/ws.js +5 -1
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/trace-checkpoint.js +12 -6
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +59 -48
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +2 -5
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -5
- package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +1 -3
- package/packages/datadog-plugin-cucumber/src/index.js +5 -1
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +51 -47
- package/packages/datadog-plugin-cypress/src/support.js +7 -4
- package/packages/datadog-plugin-electron/src/net.js +10 -4
- package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +31 -17
- package/packages/datadog-plugin-http/src/client.js +1 -1
- package/packages/datadog-plugin-http2/src/client.js +1 -1
- package/packages/datadog-plugin-jest/src/index.js +6 -22
- package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +2 -3
- package/packages/datadog-plugin-kafkajs/src/consumer.js +2 -3
- package/packages/datadog-plugin-kafkajs/src/producer.js +5 -3
- package/packages/datadog-plugin-kafkajs/src/utils.js +65 -7
- package/packages/datadog-plugin-mocha/src/index.js +832 -4
- package/packages/datadog-plugin-mysql/src/index.js +37 -0
- package/packages/datadog-plugin-next/src/index.js +51 -21
- package/packages/datadog-plugin-openai/src/tracing.js +4 -0
- package/packages/datadog-plugin-openai-agents/src/integration.js +278 -69
- package/packages/datadog-plugin-openai-agents/src/processor.js +16 -4
- package/packages/datadog-plugin-pg/src/index.js +66 -1
- package/packages/datadog-plugin-playwright/src/index.js +1 -1
- package/packages/datadog-plugin-vitest/src/index.js +103 -27
- package/packages/datadog-plugin-ws/src/util.js +2 -1
- package/packages/datadog-shimmer/src/shimmer.js +6 -2
- package/packages/dd-trace/src/aiguard/sdk.js +3 -0
- package/packages/dd-trace/src/appsec/api_security/normalized-route.js +453 -0
- package/packages/dd-trace/src/appsec/{blocking.js → blocking/index.js} +26 -6
- package/packages/dd-trace/src/appsec/handlers/auth.js +62 -0
- package/packages/dd-trace/src/appsec/handlers/http-request.js +228 -0
- package/packages/dd-trace/src/appsec/handlers/http-response.js +78 -0
- package/packages/dd-trace/src/appsec/handlers/http-shared.js +24 -0
- package/packages/dd-trace/src/appsec/handlers/payments.js +104 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/hsts-header-missing-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +1 -1
- package/packages/dd-trace/src/appsec/index.js +48 -476
- package/packages/dd-trace/src/appsec/sdk/track_event.js +9 -0
- package/packages/dd-trace/src/carrier.js +356 -0
- package/packages/dd-trace/src/ci-visibility/coverage-report-discovery.js +46 -14
- package/packages/dd-trace/src/ci-visibility/efd-retry-policy.js +89 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +27 -13
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +17 -0
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +21 -1
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +22 -7
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +9 -14
- package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +21 -5
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +1 -1
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +5 -1
- package/packages/dd-trace/src/config/generated-config-types.d.ts +5 -0
- package/packages/dd-trace/src/config/helper.js +2 -0
- package/packages/dd-trace/src/config/index.js +16 -5
- package/packages/dd-trace/src/config/remote_config.js +1 -1
- package/packages/dd-trace/src/config/supported-configurations.json +18 -0
- package/packages/dd-trace/src/datastreams/context.js +6 -1
- package/packages/dd-trace/src/datastreams/manager.js +5 -1
- package/packages/dd-trace/src/datastreams/pathway.js +23 -22
- package/packages/dd-trace/src/datastreams/processor.js +3 -2
- package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -1
- package/packages/dd-trace/src/encode/coverage-ci-visibility.js +0 -1
- package/packages/dd-trace/src/guardrails/apply-pm2-env.js +16 -0
- package/packages/dd-trace/src/guardrails/log.js +1 -10
- package/packages/dd-trace/src/llmobs/constants/tags.js +5 -0
- package/packages/dd-trace/src/llmobs/eval-metric.js +249 -0
- package/packages/dd-trace/src/llmobs/experiments/index.js +33 -25
- package/packages/dd-trace/src/llmobs/experiments/util.js +4 -2
- package/packages/dd-trace/src/llmobs/index.js +34 -2
- package/packages/dd-trace/src/llmobs/noop.js +2 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +5 -5
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +1 -1
- package/packages/dd-trace/src/llmobs/sdk.js +149 -68
- package/packages/dd-trace/src/llmobs/span_processor.js +16 -0
- package/packages/dd-trace/src/llmobs/tagger.js +96 -0
- package/packages/dd-trace/src/llmobs/telemetry.js +18 -0
- package/packages/dd-trace/src/llmobs/util.js +78 -0
- package/packages/dd-trace/src/llmobs/writers/base.js +3 -0
- package/packages/dd-trace/src/llmobs/writers/util.js +2 -0
- package/packages/dd-trace/src/log/channels.js +1 -9
- package/packages/dd-trace/src/log/levels.js +12 -0
- package/packages/dd-trace/src/openfeature/writers/exposures.js +1 -1
- package/packages/dd-trace/src/openfeature/writers/util.js +1 -1
- package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/meter.js +8 -4
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +1 -1
- package/packages/dd-trace/src/opentelemetry/span.js +4 -0
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +126 -120
- package/packages/dd-trace/src/opentracing/propagation/text_map_dsm.js +4 -8
- package/packages/dd-trace/src/plugin_manager.js +2 -0
- package/packages/dd-trace/src/plugins/ci_plugin.js +21 -5
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/util/ci.js +6 -0
- package/packages/dd-trace/src/plugins/util/git.js +6 -4
- package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +2 -2
- package/packages/dd-trace/src/plugins/util/ip_extractor.js +1 -1
- package/packages/{datadog-plugin-jest/src/util.js → dd-trace/src/plugins/util/jest.js} +3 -62
- package/packages/dd-trace/src/plugins/util/stacktrace.js +3 -0
- package/packages/dd-trace/src/plugins/util/test.js +397 -74
- package/packages/dd-trace/src/plugins/util/url.js +1 -1
- package/packages/dd-trace/src/plugins/util/web.js +76 -3
- package/packages/dd-trace/src/profiler.js +1 -1
- package/packages/dd-trace/src/profiling/exporters/event_serializer.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/events.js +2 -2
- package/packages/dd-trace/src/proxy.js +1 -0
- package/packages/dd-trace/src/ritm.js +5 -0
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +4 -2
- package/packages/dd-trace/src/sampling_rule.js +4 -2
- package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +1 -1
- package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +1 -1
- package/packages/dd-trace/src/telemetry/logs/index.js +3 -0
- package/packages/dd-trace/src/telemetry/telemetry.js +1 -1
- package/vendor/dist/pprof-format/index.js +1 -1
- package/version.js +10 -8
- /package/packages/dd-trace/src/appsec/{blocked_templates.js → blocking/templates.js} +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const METHODS = [...require('http').METHODS.map(v => v.toLowerCase()), 'all']
|
|
4
|
+
const ROUTE_RESOLUTION_FAILED = Symbol('routeResolutionFailed')
|
|
4
5
|
const shimmer = require('../../datadog-shimmer')
|
|
5
6
|
const { addHook, channel, createErrorPublisher } = require('./helpers/instrument')
|
|
6
7
|
const { getCompileToRegexp } = require('./path-to-regexp')
|
|
@@ -35,6 +36,7 @@ function isFastSlash (layer, matchers) {
|
|
|
35
36
|
* @param {{ handle: Function, name?: string, path?: string,
|
|
36
37
|
* regexp?: { fast_star?: boolean, fast_slash?: boolean } }} layer
|
|
37
38
|
* @param {Array<{ path?: string, regex?: RegExp }> & { hasStarPath?: boolean, hasSlashPath?: boolean }} matchers
|
|
39
|
+
* @returns {void}
|
|
38
40
|
*/
|
|
39
41
|
function annotateLayer (layer, matchers) {
|
|
40
42
|
const handle = layer.handle
|
|
@@ -71,6 +73,25 @@ function resolveLayerRoute (meta, layer) {
|
|
|
71
73
|
}
|
|
72
74
|
}
|
|
73
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Preserve the host Layer's error boundary while resolving a route outside its
|
|
78
|
+
* dispatch method. This path runs only for multi-pattern layers.
|
|
79
|
+
*
|
|
80
|
+
* @param {{ captureRoute?: string, needMultiMatch: boolean,
|
|
81
|
+
* matchers: Array<{ path?: string, regex?: RegExp }> }} meta
|
|
82
|
+
* @param {{ path?: string }} layer
|
|
83
|
+
* @param {Function} next
|
|
84
|
+
* @returns {string | undefined | typeof ROUTE_RESOLUTION_FAILED}
|
|
85
|
+
*/
|
|
86
|
+
function resolveLayerRouteOrForwardError (meta, layer, next) {
|
|
87
|
+
try {
|
|
88
|
+
return resolveLayerRoute(meta, layer)
|
|
89
|
+
} catch (error) {
|
|
90
|
+
next(error)
|
|
91
|
+
return ROUTE_RESOLUTION_FAILED
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
74
95
|
/**
|
|
75
96
|
* Build the request/error dispatch wrappers for one host (`express` / `router`).
|
|
76
97
|
* They wrap the layer's prototype dispatch and read the side-table metadata, so
|
|
@@ -79,6 +100,11 @@ function resolveLayerRoute (meta, layer) {
|
|
|
79
100
|
* span is published only for the layer the host actually runs.
|
|
80
101
|
*
|
|
81
102
|
* @param {string} name Channel namespace (`apm:<name>:middleware:*`).
|
|
103
|
+
* @returns {{
|
|
104
|
+
* wrapLayerRequest: (originalRequest: Function) => Function,
|
|
105
|
+
* wrapLayerError: (originalError: Function) => Function,
|
|
106
|
+
* wrapLegacyHandle: (layer: object, original: Function, guardRepeatedNext?: boolean) => Function
|
|
107
|
+
* }}
|
|
82
108
|
*/
|
|
83
109
|
function createLayerDispatchWrappers (name) {
|
|
84
110
|
const enterChannel = channel(`apm:${name}:middleware:enter`)
|
|
@@ -128,6 +154,10 @@ function createLayerDispatchWrappers (name) {
|
|
|
128
154
|
// `wrappedNext` through captures both without a tracer-side try/catch; only
|
|
129
155
|
// `exit` needs the `finally`. express 4's native dispatch converts only the
|
|
130
156
|
// synchronous throw — exactly what the pre-refactor handle wrap caught.
|
|
157
|
+
/**
|
|
158
|
+
* @param {Function} originalRequest
|
|
159
|
+
* @returns {Function}
|
|
160
|
+
*/
|
|
131
161
|
function wrapLayerRequest (originalRequest) {
|
|
132
162
|
return function (req, res, next) {
|
|
133
163
|
if (!enterChannel.hasSubscribers) return originalRequest.call(this, req, res, next)
|
|
@@ -135,8 +165,14 @@ function createLayerDispatchWrappers (name) {
|
|
|
135
165
|
const meta = getLayerMeta(this)
|
|
136
166
|
if (meta === undefined || this.handle.length > 3) return originalRequest.call(this, req, res, next)
|
|
137
167
|
|
|
168
|
+
let route = meta.captureRoute
|
|
169
|
+
if (meta.needMultiMatch) {
|
|
170
|
+
route = resolveLayerRouteOrForwardError(meta, this, next)
|
|
171
|
+
if (route === ROUTE_RESOLUTION_FAILED) return
|
|
172
|
+
}
|
|
173
|
+
|
|
138
174
|
const wrappedNext = typeof next === 'function' ? wrapNext(req, meta.name, next) : next
|
|
139
|
-
enterChannel.publish({ name: meta.name, req, route
|
|
175
|
+
enterChannel.publish({ name: meta.name, req, route, layer: this })
|
|
140
176
|
|
|
141
177
|
try {
|
|
142
178
|
return originalRequest.call(this, req, res, wrappedNext)
|
|
@@ -146,6 +182,10 @@ function createLayerDispatchWrappers (name) {
|
|
|
146
182
|
}
|
|
147
183
|
}
|
|
148
184
|
|
|
185
|
+
/**
|
|
186
|
+
* @param {Function} originalError
|
|
187
|
+
* @returns {Function}
|
|
188
|
+
*/
|
|
149
189
|
function wrapLayerError (originalError) {
|
|
150
190
|
return function (error, req, res, next) {
|
|
151
191
|
if (!enterChannel.hasSubscribers) return originalError.call(this, error, req, res, next)
|
|
@@ -153,8 +193,14 @@ function createLayerDispatchWrappers (name) {
|
|
|
153
193
|
const meta = getLayerMeta(this)
|
|
154
194
|
if (meta === undefined || this.handle.length !== 4) return originalError.call(this, error, req, res, next)
|
|
155
195
|
|
|
196
|
+
let route = meta.captureRoute
|
|
197
|
+
if (meta.needMultiMatch) {
|
|
198
|
+
route = resolveLayerRouteOrForwardError(meta, this, next)
|
|
199
|
+
if (route === ROUTE_RESOLUTION_FAILED) return
|
|
200
|
+
}
|
|
201
|
+
|
|
156
202
|
const wrappedNext = typeof next === 'function' ? wrapNext(req, meta.name, next) : next
|
|
157
|
-
enterChannel.publish({ name: meta.name, req, route
|
|
203
|
+
enterChannel.publish({ name: meta.name, req, route, layer: this })
|
|
158
204
|
|
|
159
205
|
try {
|
|
160
206
|
return originalError.call(this, error, req, res, wrappedNext)
|
|
@@ -164,36 +210,163 @@ function createLayerDispatchWrappers (name) {
|
|
|
164
210
|
}
|
|
165
211
|
}
|
|
166
212
|
|
|
167
|
-
// express <4.6.0
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
213
|
+
// express <4.6.0 dispatches `layer.handle` directly, so its replacement must preserve arity.
|
|
214
|
+
/**
|
|
215
|
+
* @param {object} layer
|
|
216
|
+
* @param {Function} original
|
|
217
|
+
* @returns {Function}
|
|
218
|
+
*/
|
|
219
|
+
function wrapNativeLegacyRequestHandle (layer, original) {
|
|
220
|
+
const meta = getLayerMeta(layer)
|
|
221
|
+
const { name, captureRoute, needMultiMatch } = meta
|
|
222
|
+
return shimmer.wrapFunction(original, inner => function (req, res, next) {
|
|
223
|
+
if (!enterChannel.hasSubscribers) return inner.call(this, req, res, next)
|
|
224
|
+
|
|
225
|
+
let calls = 0
|
|
226
|
+
if (typeof next === 'function') {
|
|
227
|
+
next = shimmer.wrapCallback(next, originalNext => function next (error) {
|
|
228
|
+
calls++
|
|
229
|
+
if (calls === 1) {
|
|
230
|
+
if (error && error !== 'route' && error !== 'router') {
|
|
231
|
+
publishError({ req, error })
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
nextChannel.publish({ req })
|
|
235
|
+
finishChannel.publish({ req })
|
|
236
|
+
} else if (calls === 2) {
|
|
237
|
+
repeatChannel.publish({ req, name, error })
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
originalNext.apply(this, arguments)
|
|
241
|
+
})
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const route = needMultiMatch ? resolveLayerRoute(meta, layer) : captureRoute
|
|
245
|
+
enterChannel.publish({ name, req, route, layer })
|
|
246
|
+
|
|
247
|
+
try {
|
|
248
|
+
return inner.call(this, req, res, next)
|
|
249
|
+
} catch (error) {
|
|
250
|
+
if (calls === 0) {
|
|
251
|
+
calls = 1
|
|
252
|
+
publishError({ req, error })
|
|
253
|
+
nextChannel.publish({ req })
|
|
254
|
+
finishChannel.publish({ req })
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
throw error
|
|
258
|
+
} finally {
|
|
259
|
+
exitChannel.publish({ req })
|
|
260
|
+
}
|
|
261
|
+
})
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* @param {object} layer
|
|
266
|
+
* @param {Function} original
|
|
267
|
+
* @returns {Function}
|
|
268
|
+
*/
|
|
269
|
+
function wrapNativeLegacyErrorHandle (layer, original) {
|
|
270
|
+
const meta = getLayerMeta(layer)
|
|
271
|
+
const { name, captureRoute, needMultiMatch } = meta
|
|
272
|
+
return shimmer.wrapFunction(original, inner => function (error, req, res, next) {
|
|
273
|
+
if (!enterChannel.hasSubscribers) return inner.call(this, error, req, res, next)
|
|
274
|
+
|
|
275
|
+
let calls = 0
|
|
276
|
+
if (typeof next === 'function') {
|
|
277
|
+
next = shimmer.wrapCallback(next, originalNext => function next (nextError) {
|
|
278
|
+
calls++
|
|
279
|
+
if (calls === 1) {
|
|
280
|
+
if (nextError && nextError !== 'route' && nextError !== 'router') {
|
|
281
|
+
publishError({ req, error: nextError })
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
nextChannel.publish({ req })
|
|
285
|
+
finishChannel.publish({ req })
|
|
286
|
+
} else if (calls === 2) {
|
|
287
|
+
repeatChannel.publish({ req, name, error: nextError })
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
originalNext.apply(this, arguments)
|
|
291
|
+
})
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const route = needMultiMatch ? resolveLayerRoute(meta, layer) : captureRoute
|
|
295
|
+
enterChannel.publish({ name, req, route, layer })
|
|
296
|
+
|
|
297
|
+
try {
|
|
298
|
+
return inner.call(this, error, req, res, next)
|
|
299
|
+
} catch (caught) {
|
|
300
|
+
if (calls === 0) {
|
|
301
|
+
calls = 1
|
|
302
|
+
publishError({ req, error: caught })
|
|
303
|
+
nextChannel.publish({ req })
|
|
304
|
+
finishChannel.publish({ req })
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
throw caught
|
|
308
|
+
} finally {
|
|
309
|
+
exitChannel.publish({ req })
|
|
310
|
+
}
|
|
311
|
+
})
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* @param {object} layer
|
|
316
|
+
* @param {Function} original
|
|
317
|
+
* @param {boolean} [guardRepeatedNext]
|
|
318
|
+
* @returns {Function}
|
|
319
|
+
*/
|
|
320
|
+
function wrapLegacyHandle (layer, original, guardRepeatedNext = false) {
|
|
321
|
+
if (!guardRepeatedNext) {
|
|
322
|
+
return original.length === 4
|
|
323
|
+
? wrapNativeLegacyErrorHandle(layer, original)
|
|
324
|
+
: wrapNativeLegacyRequestHandle(layer, original)
|
|
325
|
+
}
|
|
326
|
+
|
|
173
327
|
// `annotateLayer` always runs first in `wrapStack`, so the captured meta is
|
|
174
328
|
// never undefined here (unlike the prototype wraps, where `this` can be any
|
|
175
329
|
// layer the host dispatches).
|
|
176
330
|
const meta = getLayerMeta(layer)
|
|
331
|
+
const { name, captureRoute, needMultiMatch } = meta
|
|
177
332
|
const wrapped = shimmer.wrapFunction(original, inner => function (...args) {
|
|
178
333
|
if (!enterChannel.hasSubscribers) return inner.apply(this, args)
|
|
179
334
|
|
|
180
335
|
const isErrorHandler = original.length === 4
|
|
181
336
|
const req = args[isErrorHandler ? 1 : 0]
|
|
182
337
|
const nextIndex = isErrorHandler ? 3 : 2
|
|
183
|
-
|
|
338
|
+
let calls = 0
|
|
339
|
+
if (typeof args[nextIndex] === 'function') {
|
|
340
|
+
args[nextIndex] = shimmer.wrapCallback(args[nextIndex], originalNext => function next (error) {
|
|
341
|
+
calls++
|
|
342
|
+
if (calls === 1) {
|
|
343
|
+
if (error && error !== 'route' && error !== 'router') {
|
|
344
|
+
publishError({ req, error })
|
|
345
|
+
}
|
|
184
346
|
|
|
185
|
-
|
|
347
|
+
nextChannel.publish({ req })
|
|
348
|
+
finishChannel.publish({ req })
|
|
349
|
+
} else if (calls === 2) {
|
|
350
|
+
repeatChannel.publish({ req, name, error })
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
originalNext.apply(this, arguments)
|
|
354
|
+
})
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const route = needMultiMatch ? resolveLayerRoute(meta, layer) : captureRoute
|
|
358
|
+
enterChannel.publish({ name, req, route, layer })
|
|
186
359
|
|
|
187
360
|
try {
|
|
188
361
|
return inner.apply(this, args)
|
|
189
362
|
} catch (error) {
|
|
190
|
-
//
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
363
|
+
// Legacy hosts catch outside the layer and never call its wrapped `next`, so finish before rethrowing.
|
|
364
|
+
if (calls === 0) {
|
|
365
|
+
calls = 1
|
|
366
|
+
publishError({ req, error })
|
|
367
|
+
nextChannel.publish({ req })
|
|
368
|
+
finishChannel.publish({ req })
|
|
369
|
+
}
|
|
197
370
|
|
|
198
371
|
throw error
|
|
199
372
|
} finally {
|
|
@@ -209,6 +382,7 @@ function createLayerDispatchWrappers (name) {
|
|
|
209
382
|
|
|
210
383
|
/**
|
|
211
384
|
* @param {{ handle_request?: unknown, handleRequest?: unknown }} layer
|
|
385
|
+
* @returns {boolean}
|
|
212
386
|
*/
|
|
213
387
|
function hasLayerDispatch (layer) {
|
|
214
388
|
return typeof layer.handle_request === 'function' || typeof layer.handleRequest === 'function'
|
|
@@ -221,9 +395,11 @@ function hasLayerDispatch (layer) {
|
|
|
221
395
|
* Host-resolved path-to-regexp compile adapter, or undefined when the host
|
|
222
396
|
* instance ships no path-to-regexp. Captured here so each express/router
|
|
223
397
|
* instance keeps the dialect it actually loaded.
|
|
224
|
-
* @param {((layer: object, original: Function) => Function) | undefined}
|
|
398
|
+
* @param {((layer: object, original: Function, guardRepeatedNext?: boolean) => Function) | undefined}
|
|
399
|
+
* [wrapLegacyHandle]
|
|
225
400
|
* Fallback that replaces `layer.handle` for hosts without a `Layer` prototype
|
|
226
401
|
* dispatch (express <4.6.0). Omitted for hosts that always ship one.
|
|
402
|
+
* @returns {(original: Function) => Function}
|
|
227
403
|
*/
|
|
228
404
|
function createWrapRouterMethod (name, compile, wrapLegacyHandle) {
|
|
229
405
|
const routeAddedChannel = channel(`apm:${name}:route:added`)
|
|
@@ -234,7 +410,7 @@ function createWrapRouterMethod (name, compile, wrapLegacyHandle) {
|
|
|
234
410
|
|
|
235
411
|
if (wrapLegacyHandle !== undefined && !hasLayerDispatch(layer)) {
|
|
236
412
|
if (layer.__handle) { // express-async-errors
|
|
237
|
-
layer.__handle = wrapLegacyHandle(layer, layer.__handle)
|
|
413
|
+
layer.__handle = wrapLegacyHandle(layer, layer.__handle, true)
|
|
238
414
|
} else {
|
|
239
415
|
layer.handle = wrapLegacyHandle(layer, layer.handle)
|
|
240
416
|
}
|
|
@@ -4,14 +4,15 @@ const path = require('node:path')
|
|
|
4
4
|
|
|
5
5
|
const satisfies = require('../../../vendor/dist/semifies')
|
|
6
6
|
|
|
7
|
+
const { hasEfdRetries } = require('../../dd-trace/src/ci-visibility/efd-retry-policy')
|
|
7
8
|
const { RUM_TEST_EXECUTION_ID_COOKIE_NAME } = require('../../dd-trace/src/ci-visibility/rum')
|
|
8
9
|
const { getValueFromEnvSources } = require('../../dd-trace/src/config/helper')
|
|
9
10
|
const log = require('../../dd-trace/src/log')
|
|
10
11
|
const {
|
|
11
12
|
DYNAMIC_NAME_RE,
|
|
12
|
-
EARLY_FLAKE_DETECTION_RETRY_THRESHOLDS,
|
|
13
13
|
getTestSuitePath,
|
|
14
14
|
logAttemptToFixTestExecution,
|
|
15
|
+
recordTestManagementExecution,
|
|
15
16
|
recordAttemptToFixExecution,
|
|
16
17
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
17
18
|
const {
|
|
@@ -46,6 +47,7 @@ const VITEST_NO_WORKER_INIT_ACTIVE_ENV = 'DD_TEST_OPT_VITEST_NO_WORKER_INIT_ACTI
|
|
|
46
47
|
const VITEST_NO_WORKER_INIT_REQUEST_ENV = 'DD_EXPERIMENTAL_TEST_OPT_VITEST_NO_WORKER_INIT'
|
|
47
48
|
const VITEST_NO_WORKER_INIT_MINIMUM_VERSION = '3.2.6'
|
|
48
49
|
const VITEST_DEFAULT_POOL = 'forks'
|
|
50
|
+
const VITEST_BROWSER_EFD_SUITE_ADMISSION_COMMAND = '__dd_vitest_efd_suite_admission'
|
|
49
51
|
const VITEST_NO_WORKER_INIT_SETUP_FILE = path.join(
|
|
50
52
|
__dirname,
|
|
51
53
|
'..',
|
|
@@ -68,6 +70,7 @@ const VITEST_BROWSER_URL_RE = /https?:\/\/[^\s)"'>\]]+/g
|
|
|
68
70
|
let hasWarnedDisabledIsolate = false
|
|
69
71
|
let hasWarnedUnsupportedVersion = false
|
|
70
72
|
let nodeOptionsBeforeNoWorkerInit
|
|
73
|
+
let reserveEarlyFlakeDetectionSuite
|
|
71
74
|
|
|
72
75
|
function noop () {}
|
|
73
76
|
|
|
@@ -309,8 +312,19 @@ function isNoWorkerInitPool (pool, isVitestWorkerPool) {
|
|
|
309
312
|
return pool === undefined || isVitestWorkerPool(pool)
|
|
310
313
|
}
|
|
311
314
|
|
|
315
|
+
/**
|
|
316
|
+
* @param {object} state
|
|
317
|
+
* @returns {boolean}
|
|
318
|
+
*/
|
|
319
|
+
function isEarlyFlakeDetectionActive (state) {
|
|
320
|
+
return state.isEarlyFlakeDetectionEnabled &&
|
|
321
|
+
!state.isEarlyFlakeDetectionFaulty &&
|
|
322
|
+
hasEfdRetries(state.earlyFlakeDetectionRetryPolicy)
|
|
323
|
+
}
|
|
324
|
+
|
|
312
325
|
function configure (ctx, frameworkVersion, testSpecifications, setupData, options) {
|
|
313
|
-
const {
|
|
326
|
+
const { shouldReportTestModule, state } = options
|
|
327
|
+
reserveEarlyFlakeDetectionSuite = options.reserveEarlyFlakeDetectionSuite
|
|
314
328
|
addSetupFileToVitestConfigs(ctx, VITEST_NO_WORKER_INIT_SETUP_FILE, testSpecifications)
|
|
315
329
|
addVitestBrowserSetupFileAccess(testSpecifications)
|
|
316
330
|
|
|
@@ -329,13 +343,10 @@ function configure (ctx, frameworkVersion, testSpecifications, setupData, option
|
|
|
329
343
|
attemptToFixRetries: state.testManagementAttemptToFixRetries,
|
|
330
344
|
attemptToFixTests: getSelectedTestManagementTests(testManagementTestsBySuite, 'isAttemptToFix'),
|
|
331
345
|
disabledTests: getSelectedTestManagementTests(testManagementTestsBySuite, 'isDisabled'),
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
),
|
|
336
|
-
earlyFlakeDetectionRetryThresholds: EARLY_FLAKE_DETECTION_RETRY_THRESHOLDS,
|
|
337
|
-
earlyFlakeDetectionSlowRetries: state.earlyFlakeDetectionSlowTestRetries,
|
|
338
|
-
isEarlyFlakeDetectionEnabled: state.isEarlyFlakeDetectionEnabled && !state.isEarlyFlakeDetectionFaulty,
|
|
346
|
+
earlyFlakeDetectionRetryPolicy: state.earlyFlakeDetectionRetryPolicy,
|
|
347
|
+
efdSuiteAdmissionBrowserCommand: VITEST_BROWSER_EFD_SUITE_ADMISSION_COMMAND,
|
|
348
|
+
isEfdSuiteAdmissionEnabled: state.isEfdSuiteAdmissionEnabled,
|
|
349
|
+
isEarlyFlakeDetectionEnabled: isEarlyFlakeDetectionActive(state),
|
|
339
350
|
isRumCorrelationEnabled: !canRaceRumCorrelation(ctx, testSpecifications),
|
|
340
351
|
knownTests: knownTestsBySuite || {},
|
|
341
352
|
modifiedFiles: modifiedFiles || {},
|
|
@@ -437,6 +448,18 @@ function configureVitestBrowserSetupFile (viteConfig) {
|
|
|
437
448
|
}
|
|
438
449
|
}
|
|
439
450
|
|
|
451
|
+
/**
|
|
452
|
+
* Reserves EFD retries for a suite executing in Vitest Browser Mode.
|
|
453
|
+
*
|
|
454
|
+
* @param {unknown} _context
|
|
455
|
+
* @param {string} testSuite
|
|
456
|
+
* @param {unknown} hasNewTest
|
|
457
|
+
* @returns {boolean}
|
|
458
|
+
*/
|
|
459
|
+
function handleBrowserEfdSuiteAdmission (_context, testSuite, hasNewTest) {
|
|
460
|
+
return reserveEarlyFlakeDetectionSuite?.(testSuite, hasNewTest === true) === true
|
|
461
|
+
}
|
|
462
|
+
|
|
440
463
|
/**
|
|
441
464
|
* Allows Vite to serve the Datadog setup file after its default workspace detection has run.
|
|
442
465
|
*
|
|
@@ -445,7 +468,9 @@ function configureVitestBrowserSetupFile (viteConfig) {
|
|
|
445
468
|
*/
|
|
446
469
|
function allowVitestBrowserSetupFile (viteConfig) {
|
|
447
470
|
const allow = viteConfig.server?.fs?.allow
|
|
448
|
-
if (
|
|
471
|
+
if (!allow) return
|
|
472
|
+
|
|
473
|
+
if (!allow.includes(VITEST_NO_WORKER_INIT_SETUP_FILE)) {
|
|
449
474
|
allow.push(VITEST_NO_WORKER_INIT_SETUP_FILE)
|
|
450
475
|
}
|
|
451
476
|
}
|
|
@@ -465,10 +490,16 @@ function addVitestBrowserSetupFileAccess (testSpecifications) {
|
|
|
465
490
|
if (!isBrowserTestSpecification(testSpecification)) continue
|
|
466
491
|
|
|
467
492
|
const browserServerProject = project?._parent || project
|
|
468
|
-
if (!browserServerProject
|
|
493
|
+
if (!browserServerProject) continue
|
|
494
|
+
|
|
495
|
+
addVitestBrowserCommand(safeConfig(project))
|
|
496
|
+
if (configuredProjects.has(browserServerProject)) continue
|
|
469
497
|
|
|
470
498
|
configuredProjects.add(browserServerProject)
|
|
499
|
+
addVitestBrowserCommand(safeConfig(browserServerProject))
|
|
471
500
|
browserServerProject.options ||= {}
|
|
501
|
+
browserServerProject.options.browser ||= {}
|
|
502
|
+
addVitestBrowserCommand(browserServerProject.options)
|
|
472
503
|
browserServerProject.options.plugins ||= []
|
|
473
504
|
if (!browserServerProject.options.plugins.includes(VITEST_BROWSER_SETUP_FILE_PLUGIN)) {
|
|
474
505
|
browserServerProject.options.plugins.push(VITEST_BROWSER_SETUP_FILE_PLUGIN)
|
|
@@ -476,6 +507,20 @@ function addVitestBrowserSetupFileAccess (testSpecifications) {
|
|
|
476
507
|
}
|
|
477
508
|
}
|
|
478
509
|
|
|
510
|
+
/**
|
|
511
|
+
* Adds the private EFD admission command to a Vitest Browser Mode configuration.
|
|
512
|
+
*
|
|
513
|
+
* @param {object|undefined} config
|
|
514
|
+
* @returns {void}
|
|
515
|
+
*/
|
|
516
|
+
function addVitestBrowserCommand (config) {
|
|
517
|
+
const browserConfig = config?.browser
|
|
518
|
+
if (!browserConfig) return
|
|
519
|
+
|
|
520
|
+
browserConfig.commands ||= {}
|
|
521
|
+
browserConfig.commands[VITEST_BROWSER_EFD_SUITE_ADMISSION_COMMAND] = handleBrowserEfdSuiteAdmission
|
|
522
|
+
}
|
|
523
|
+
|
|
479
524
|
function getNoWorkerInitVitestConfigs (ctx, testSpecifications) {
|
|
480
525
|
const configs = new Set()
|
|
481
526
|
if (Array.isArray(testSpecifications)) {
|
|
@@ -841,7 +886,7 @@ function createMainProcessReporter (reporterState) {
|
|
|
841
886
|
!isAttemptToFix &&
|
|
842
887
|
state.isKnownTestsEnabled &&
|
|
843
888
|
knownTests &&
|
|
844
|
-
!state.isEarlyFlakeDetectionFaulty &&
|
|
889
|
+
(state.isEfdSuiteAdmissionEnabled || !state.isEarlyFlakeDetectionFaulty) &&
|
|
845
890
|
!knownTests.includes(testName)
|
|
846
891
|
)
|
|
847
892
|
const isModified = testProperties?.isModified === true
|
|
@@ -855,7 +900,7 @@ function createMainProcessReporter (reporterState) {
|
|
|
855
900
|
return {
|
|
856
901
|
isAttemptToFix,
|
|
857
902
|
isDisabled: testManagementProperties.isDisabled,
|
|
858
|
-
isEarlyFlakeDetection:
|
|
903
|
+
isEarlyFlakeDetection: task.meta?.__ddTestOptEfdRetries !== undefined,
|
|
859
904
|
isFlakyTestRetries,
|
|
860
905
|
isQuarantined: testManagementProperties.isQuarantined,
|
|
861
906
|
isModified,
|
|
@@ -1176,6 +1221,23 @@ function reportFinalTestAttempt (testReport) {
|
|
|
1176
1221
|
} = testReport
|
|
1177
1222
|
|
|
1178
1223
|
if (status === 'skip') {
|
|
1224
|
+
recordTestManagementExecution({
|
|
1225
|
+
testSuite: testProperties.testSuite,
|
|
1226
|
+
testName,
|
|
1227
|
+
status,
|
|
1228
|
+
isAttemptToFix: testProperties.isAttemptToFix,
|
|
1229
|
+
isDisabled: testProperties.isDisabled,
|
|
1230
|
+
isQuarantined: testProperties.isQuarantined,
|
|
1231
|
+
})
|
|
1232
|
+
if (testProperties.isAttemptToFix) {
|
|
1233
|
+
recordAttemptToFixExecution(state.attemptToFixExecutions, {
|
|
1234
|
+
testSuite: testProperties.testSuite,
|
|
1235
|
+
testName,
|
|
1236
|
+
status,
|
|
1237
|
+
isDisabled: testProperties.isDisabled,
|
|
1238
|
+
isQuarantined: testProperties.isQuarantined,
|
|
1239
|
+
})
|
|
1240
|
+
}
|
|
1179
1241
|
const {
|
|
1180
1242
|
isRumActive,
|
|
1181
1243
|
testExecutionId,
|
|
@@ -1185,7 +1247,9 @@ function reportFinalTestAttempt (testReport) {
|
|
|
1185
1247
|
testName,
|
|
1186
1248
|
testSuiteAbsolutePath,
|
|
1187
1249
|
isNew: testProperties.isNew,
|
|
1250
|
+
isAttemptToFix: testProperties.isAttemptToFix,
|
|
1188
1251
|
isDisabled: testProperties.isDisabled,
|
|
1252
|
+
isQuarantined: testProperties.isQuarantined,
|
|
1189
1253
|
isRumActive,
|
|
1190
1254
|
isTestFrameworkWorker: true,
|
|
1191
1255
|
requestErrorTags: state.requestErrorTags,
|
|
@@ -1282,6 +1346,14 @@ function reportTestAttempt (testReport, attempt) {
|
|
|
1282
1346
|
testStartLine: task.location?.line,
|
|
1283
1347
|
testExecutionId,
|
|
1284
1348
|
}
|
|
1349
|
+
recordTestManagementExecution({
|
|
1350
|
+
testSuite: testProperties.testSuite,
|
|
1351
|
+
testName,
|
|
1352
|
+
status,
|
|
1353
|
+
isAttemptToFix: testProperties.isAttemptToFix,
|
|
1354
|
+
isDisabled: testProperties.isDisabled,
|
|
1355
|
+
isQuarantined: testProperties.isQuarantined,
|
|
1356
|
+
})
|
|
1285
1357
|
if (testProperties.isAttemptToFix) {
|
|
1286
1358
|
recordAttemptToFixExecution(state.attemptToFixExecutions, {
|
|
1287
1359
|
testSuite: testProperties.testSuite,
|