dd-trace 5.132.0 → 5.134.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 +9 -14
- package/ci/init.js +1 -2
- package/ci/test-optimization-validation/ci-discovery.js +9 -4
- package/ci/test-optimization-validation/executable.js +1 -5
- package/ci/test-optimization-validation/framework-adapters/cypress.js +1 -4
- package/ci/test-optimization-validation/framework-adapters/vitest.js +1 -1
- package/ci/test-optimization-validation/generated-files.js +6 -4
- package/ci/test-optimization-validation/generated-test-contract.js +7 -3
- package/ci/test-optimization-validation/manifest-scaffold.js +7 -2
- package/ci/test-optimization-validation/manifest-schema.js +6 -7
- package/ci/test-optimization-validation/report-writer.js +6 -2
- package/ci/test-optimization-validation/runner-command.js +2 -4
- package/ci/test-optimization-validation/runner-contract.js +1 -2
- package/ci/test-optimization-validation/scenarios/auto-test-retries.js +1 -15
- package/ci/vitest-no-worker-init-runner-setup.mjs +27 -0
- package/ci/vitest-no-worker-init-setup.mjs +10 -7
- package/index.d.ts +41 -19
- package/initialize.mjs +1 -3
- package/openfeature/rules-based.d.ts +11 -0
- package/openfeature/rules-based.js +7 -0
- package/package.json +25 -22
- package/packages/datadog-core/src/storage.js +16 -0
- package/packages/datadog-esbuild/index.js +18 -12
- package/packages/datadog-instrumentations/index.js +2 -1
- package/packages/datadog-instrumentations/src/aerospike.js +3 -3
- package/packages/datadog-instrumentations/src/ai.js +3 -3
- package/packages/datadog-instrumentations/src/aws-durable-execution-sdk-js.js +0 -6
- package/packages/datadog-instrumentations/src/aws-sdk.js +21 -13
- package/packages/datadog-instrumentations/src/cassandra-driver.js +25 -20
- package/packages/datadog-instrumentations/src/claude-agent-sdk.js +9 -23
- package/packages/datadog-instrumentations/src/cucumber.js +65 -41
- package/packages/datadog-instrumentations/src/elasticsearch.js +1 -5
- package/packages/datadog-instrumentations/src/electron.js +6 -6
- package/packages/datadog-instrumentations/src/fastify.js +13 -4
- package/packages/datadog-instrumentations/src/fs.js +2 -2
- package/packages/datadog-instrumentations/src/google-cloud-pubsub.js +9 -5
- package/packages/datadog-instrumentations/src/grpc/client.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/bundler-register.js +28 -9
- package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +1 -2
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/instrument.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/register.js +45 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentation-registry.js +89 -6
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql-jit.js +5 -6
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/jest.js +12 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/postgres.js +48 -31
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/vitest.js +8 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +7 -7
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +5 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms/postgres.js +42 -4
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +7 -10
- package/packages/datadog-instrumentations/src/ioredis.js +7 -5
- package/packages/datadog-instrumentations/src/iovalkey.js +7 -5
- package/packages/datadog-instrumentations/src/jest.js +72 -67
- package/packages/datadog-instrumentations/src/knex.js +9 -5
- package/packages/datadog-instrumentations/src/mariadb-bundle.js +3 -3
- package/packages/datadog-instrumentations/src/mocha/common.js +20 -8
- package/packages/datadog-instrumentations/src/mocha/main.js +140 -43
- package/packages/datadog-instrumentations/src/mocha/utils.js +38 -4
- package/packages/datadog-instrumentations/src/mocha/worker.js +1 -2
- package/packages/datadog-instrumentations/src/mongodb-core.js +3 -3
- package/packages/datadog-instrumentations/src/mongoose.js +3 -3
- package/packages/datadog-instrumentations/src/nova-sonic/audio.js +173 -0
- package/packages/datadog-instrumentations/src/nova-sonic/index.js +209 -0
- package/packages/datadog-instrumentations/src/nova-sonic/session.js +492 -0
- package/packages/datadog-instrumentations/src/openai-realtime/index.js +18 -6
- package/packages/datadog-instrumentations/src/openai-realtime/session.js +1 -1
- package/packages/datadog-instrumentations/src/openai.js +7 -0
- package/packages/datadog-instrumentations/src/pino.js +5 -6
- package/packages/datadog-instrumentations/src/playwright.js +2 -0
- package/packages/datadog-instrumentations/src/redis.js +27 -13
- package/packages/datadog-instrumentations/src/router.js +30 -0
- package/packages/datadog-instrumentations/src/rum-browser-scripts.js +2 -6
- package/packages/datadog-instrumentations/src/stripe.js +23 -12
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +8 -5
- package/packages/datadog-instrumentations/src/vitest-main.js +16 -18
- package/packages/datadog-instrumentations/src/vitest-util.js +7 -4
- package/packages/datadog-instrumentations/src/vitest-worker.js +1 -2
- package/packages/datadog-instrumentations/src/vitest.js +9 -1
- package/packages/datadog-instrumentations/src/webdriverio.js +5 -8
- package/packages/datadog-instrumentations/src/ws.js +1 -4
- package/packages/datadog-plugin-amqplib/src/client.js +1 -2
- package/packages/datadog-plugin-avsc/src/schema_iterator.js +1 -5
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/handler.js +1 -2
- package/packages/datadog-plugin-aws-sdk/src/base.js +5 -8
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/index.js +18 -0
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/sonic.js +45 -0
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +9 -3
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +2 -5
- package/packages/datadog-plugin-azure-durable-functions/src/index.js +5 -4
- package/packages/datadog-plugin-azure-functions/src/index.js +5 -3
- package/packages/datadog-plugin-claude-agent-sdk/src/index.js +0 -2
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +4 -5
- package/packages/datadog-plugin-cypress/src/index.js +6 -6
- package/packages/datadog-plugin-cypress/src/source-map-utils.js +1 -2
- package/packages/datadog-plugin-cypress/src/support.js +3 -4
- package/packages/datadog-plugin-express/src/code_origin.js +1 -2
- package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -2
- package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +1 -2
- package/packages/datadog-plugin-graphql/src/execute.js +5 -5
- package/packages/datadog-plugin-grpc/src/util.js +1 -2
- package/packages/datadog-plugin-kafkajs/src/producer.js +1 -2
- package/packages/datadog-plugin-mocha/src/index.js +7 -10
- package/packages/datadog-plugin-mocha/src/webdriverio-video.js +8 -4
- package/packages/datadog-plugin-mongodb-core/src/query.js +8 -5
- package/packages/datadog-plugin-next/src/index.js +1 -2
- package/packages/datadog-plugin-openai/src/tracing.js +5 -2
- package/packages/datadog-plugin-openai-agents/src/integration.js +6 -2
- package/packages/datadog-plugin-openai-agents/src/processor.js +2 -4
- package/packages/datadog-plugin-postgres/src/index.js +24 -0
- package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +1 -5
- package/packages/datadog-plugin-rhea/src/consumer.js +1 -4
- package/packages/datadog-plugin-vitest/src/index.js +5 -2
- package/packages/datadog-shimmer/src/shimmer.js +5 -4
- package/packages/datadog-webpack/index.js +55 -43
- package/packages/datadog-webpack/src/loader.js +56 -8
- package/packages/dd-trace/src/aiguard/evaluation.js +143 -1
- package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +2 -4
- package/packages/dd-trace/src/aiguard/messages/utils.js +1 -2
- package/packages/dd-trace/src/aiguard/redaction.js +8 -2
- package/packages/dd-trace/src/appsec/api_security/normalized-route.js +3 -2
- package/packages/dd-trace/src/appsec/api_security/sampler.js +2 -6
- package/packages/dd-trace/src/appsec/downstream_requests.js +1 -9
- package/packages/dd-trace/src/appsec/handlers/http-request.js +3 -9
- package/packages/dd-trace/src/appsec/handlers/http-response.js +7 -2
- package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +4 -3
- package/packages/dd-trace/src/appsec/iast/analyzers/set-cookies-header-interceptor.js +1 -4
- package/packages/dd-trace/src/appsec/iast/path-line.js +2 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/operations-taint-object.js +7 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +1 -3
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-esm.mjs +1 -3
- package/packages/dd-trace/src/appsec/iast/telemetry/iast-metric.js +1 -6
- package/packages/dd-trace/src/appsec/lambda.js +1 -3
- package/packages/dd-trace/src/appsec/rasp/fs-plugin.js +37 -27
- package/packages/dd-trace/src/appsec/rasp/lfi.js +9 -5
- package/packages/dd-trace/src/appsec/reporter.js +21 -1
- package/packages/dd-trace/src/appsec/stack_trace.js +4 -4
- package/packages/dd-trace/src/ci-visibility/dynamic-atr-retries.js +3 -6
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +3 -2
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/msgpack-to-json.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +13 -7
- package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +1 -2
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +2 -8
- package/packages/dd-trace/src/ci-visibility/resolve-runfile-path.js +54 -0
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +5 -8
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +2 -53
- package/packages/dd-trace/src/config/config-types.d.ts +2 -2
- package/packages/dd-trace/src/config/defaults.js +5 -5
- package/packages/dd-trace/src/config/generated-config-types.d.ts +10 -0
- package/packages/dd-trace/src/config/helper.js +34 -3
- package/packages/dd-trace/src/config/index.js +55 -9
- package/packages/dd-trace/src/config/normalize-service.js +1 -1
- package/packages/dd-trace/src/config/parsers.js +2 -2
- package/packages/dd-trace/src/config/supported-configurations.json +44 -0
- package/packages/dd-trace/src/datastreams/processor.js +3 -2
- package/packages/dd-trace/src/datastreams/schemas/schema_builder.js +1 -4
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +6 -5
- package/packages/dd-trace/src/debugger/devtools_client/condition.js +5 -5
- package/packages/dd-trace/src/debugger/devtools_client/send.js +1 -0
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/collector.js +4 -6
- package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -0
- package/packages/dd-trace/src/debugger/guardrail-metrics.js +2 -2
- package/packages/dd-trace/src/debugger/unsupported-runtime.js +16 -0
- package/packages/dd-trace/src/dogstatsd.js +1 -1
- package/packages/dd-trace/src/encode/tags-processors.js +2 -2
- package/packages/dd-trace/src/exporters/common/request.js +1 -5
- package/packages/dd-trace/src/id.js +14 -1
- package/packages/dd-trace/src/lambda/handler.js +179 -19
- package/packages/dd-trace/src/lambda/index.js +15 -1
- package/packages/dd-trace/src/lambda/runtime/patch.js +34 -7
- package/packages/dd-trace/src/llmobs/agent-manifest.js +19 -2
- package/packages/dd-trace/src/llmobs/audio-utils.js +3 -2
- package/packages/dd-trace/src/llmobs/constants/tags.js +15 -0
- package/packages/dd-trace/src/llmobs/experiments/experiment.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +15 -0
- package/packages/dd-trace/src/llmobs/plugins/genai/util.js +1 -2
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chat_model.js +1 -3
- package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/nova-sonic.js +92 -0
- package/packages/dd-trace/src/llmobs/plugins/openai/constants.js +5 -0
- package/packages/dd-trace/src/llmobs/plugins/openai/index.js +64 -2
- package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +38 -0
- package/packages/dd-trace/src/llmobs/prompts/manager.js +5 -7
- package/packages/dd-trace/src/llmobs/prompts/prompt.js +1 -1
- package/packages/dd-trace/src/llmobs/sdk.js +15 -4
- package/packages/dd-trace/src/llmobs/span_processor.js +13 -2
- package/packages/dd-trace/src/llmobs/tagger.js +20 -10
- package/packages/dd-trace/src/llmobs/util.js +11 -6
- package/packages/dd-trace/src/llmobs/writers/base.js +4 -5
- package/packages/dd-trace/src/log/log.js +1 -5
- package/packages/dd-trace/src/openfeature/evaluation-telemetry-opt-out-hook.js +51 -0
- package/packages/dd-trace/src/openfeature/flagging_provider.js +48 -21
- package/packages/dd-trace/src/openfeature/index.js +6 -0
- package/packages/dd-trace/src/openfeature/writers/util.js +4 -4
- package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_transformer.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +1 -2
- package/packages/dd-trace/src/opentelemetry/otlp/common.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +24 -24
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +53 -13
- package/packages/dd-trace/src/otel-sampling.js +4 -5
- package/packages/dd-trace/src/otel-thread-ctx.js +79 -13
- package/packages/dd-trace/src/plugin_manager.js +1 -2
- package/packages/dd-trace/src/plugins/database.js +7 -16
- package/packages/dd-trace/src/plugins/tracing.js +0 -1
- package/packages/dd-trace/src/plugins/util/git.js +99 -59
- package/packages/dd-trace/src/plugins/util/inferred_proxy.js +1 -5
- package/packages/dd-trace/src/plugins/util/stacktrace.js +2 -1
- package/packages/dd-trace/src/plugins/util/test.js +50 -4
- package/packages/dd-trace/src/plugins/util/web.js +90 -20
- package/packages/dd-trace/src/priority_sampler.js +1 -2
- package/packages/dd-trace/src/profiling/profiler.js +4 -6
- package/packages/dd-trace/src/proxy.js +1 -2
- package/packages/dd-trace/src/ritm.js +4 -2
- package/packages/dd-trace/src/serverless.js +5 -0
- package/packages/dd-trace/src/span_processor.js +13 -17
- package/packages/dd-trace/src/span_stats.js +4 -1
- package/packages/dd-trace/src/telemetry/endpoints.js +1 -3
- package/packages/dd-trace/src/telemetry/logs/log-collector.js +1 -1
- package/packages/dd-trace/src/util.js +37 -4
- package/packages/dd-trace/src/web-tags-cache.js +6 -2
- package/vendor/dist/@apm-js-collab/code-transformer/index.js +8 -8
- package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
- package/vendor/dist/@datadog/openfeature-node-server/rules-based/LICENSE +201 -0
- package/vendor/dist/@datadog/openfeature-node-server/rules-based/index.d.ts +925 -0
- package/vendor/dist/@datadog/openfeature-node-server/rules-based/index.js +1 -0
- package/vendor/dist/@datadog/sketches-js/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
|
@@ -102,7 +102,7 @@ const efdRetryCountByPickleId = new Map()
|
|
|
102
102
|
const efdSlowAbortedPickleIds = new Set()
|
|
103
103
|
const finishedParallelSuites = new Set()
|
|
104
104
|
const numAttemptToCtx = new Map()
|
|
105
|
-
const
|
|
105
|
+
const efdStatusesByPickleId = new Map()
|
|
106
106
|
const attemptToFixTestsByTestFullname = new Map()
|
|
107
107
|
const modifiedTestsByPickleId = new Map()
|
|
108
108
|
const runnerToRetryState = new WeakMap()
|
|
@@ -129,6 +129,7 @@ let earlyFlakeDetectionRetryPolicy = EMPTY_EFD_RETRY_POLICY
|
|
|
129
129
|
let earlyFlakeDetectionFaultyThreshold = 0
|
|
130
130
|
let isEarlyFlakeDetectionFaulty = false
|
|
131
131
|
let isFlakyTestRetriesEnabled = false
|
|
132
|
+
let hasAutomaticRetries = false
|
|
132
133
|
let isDynamicAtrEnabled = false
|
|
133
134
|
let dynamicAtrBuckets
|
|
134
135
|
let isKnownTestsEnabled = false
|
|
@@ -200,13 +201,13 @@ function getSuiteStatusFromTestStatuses (testStatuses) {
|
|
|
200
201
|
return 'pass'
|
|
201
202
|
}
|
|
202
203
|
|
|
203
|
-
function publishWorkerEfdRetryCount (pickle, retryCount) {
|
|
204
|
+
function publishWorkerEfdRetryCount (pickle, retryCount, isModified) {
|
|
204
205
|
const message = {
|
|
205
206
|
[DD_EFD_RETRY_COUNT_MESSAGE]: {
|
|
206
207
|
pickleId: pickle.id,
|
|
207
208
|
retryCount,
|
|
209
|
+
isModified,
|
|
208
210
|
testFileAbsolutePath: pickle.uri,
|
|
209
|
-
testName: pickle.name,
|
|
210
211
|
},
|
|
211
212
|
}
|
|
212
213
|
|
|
@@ -220,10 +221,12 @@ function publishWorkerEfdRetryCount (pickle, retryCount) {
|
|
|
220
221
|
}
|
|
221
222
|
|
|
222
223
|
try {
|
|
223
|
-
const { isMainThread, parentPort } = require('node:worker_threads')
|
|
224
|
+
const { isMainThread, parentPort, workerData } = require('node:worker_threads')
|
|
224
225
|
if (isMainThread || !parentPort) return
|
|
225
226
|
|
|
226
|
-
|
|
227
|
+
// Share Cucumber's envelope port so EFD metadata arrives before test results.
|
|
228
|
+
const port = workerData?.port ?? parentPort
|
|
229
|
+
port.postMessage(message)
|
|
227
230
|
} catch {
|
|
228
231
|
// ignore IPC errors
|
|
229
232
|
}
|
|
@@ -253,6 +256,7 @@ function configureParallelWorkerWorldParameters (options) {
|
|
|
253
256
|
}
|
|
254
257
|
|
|
255
258
|
options.worldParameters._ddIsFlakyTestRetriesEnabled = isFlakyTestRetriesEnabled
|
|
259
|
+
options.worldParameters._ddHasAutomaticRetries = hasAutomaticRetries
|
|
256
260
|
options.worldParameters._ddNumTestRetries = numTestRetries
|
|
257
261
|
options.worldParameters._ddIsDynamicAtrEnabled = isDynamicAtrEnabled
|
|
258
262
|
options.worldParameters._ddDynamicAtrBuckets = dynamicAtrBuckets
|
|
@@ -296,6 +300,7 @@ function readParallelWorkerWorldParameters (options) {
|
|
|
296
300
|
/**
|
|
297
301
|
* @param {object} worldParameters
|
|
298
302
|
* @param {boolean} [worldParameters._ddIsFlakyTestRetriesEnabled]
|
|
303
|
+
* @param {boolean} [worldParameters._ddHasAutomaticRetries]
|
|
299
304
|
* @param {number} [worldParameters._ddNumTestRetries]
|
|
300
305
|
* @param {boolean} [worldParameters._ddIsDynamicAtrEnabled]
|
|
301
306
|
* @param {number[]} [worldParameters._ddDynamicAtrBuckets]
|
|
@@ -304,6 +309,7 @@ function readParallelWorkerWorldParameters (options) {
|
|
|
304
309
|
*/
|
|
305
310
|
function readParallelWorkerAtrParameters (worldParameters) {
|
|
306
311
|
isFlakyTestRetriesEnabled = !!worldParameters._ddIsFlakyTestRetriesEnabled
|
|
312
|
+
hasAutomaticRetries = !!worldParameters._ddHasAutomaticRetries
|
|
307
313
|
numTestRetries = worldParameters._ddNumTestRetries ?? 0
|
|
308
314
|
isDynamicAtrEnabled = !!worldParameters._ddIsDynamicAtrEnabled
|
|
309
315
|
dynamicAtrBuckets = worldParameters._ddDynamicAtrBuckets
|
|
@@ -385,14 +391,16 @@ function handleParallelTestCaseFinished (pickle, worstTestStepResult, usesNumeri
|
|
|
385
391
|
const testFileAbsolutePath = pickle.uri
|
|
386
392
|
const finished = (pickleResultByFile[testFileAbsolutePath] ||= [])
|
|
387
393
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
394
|
+
const isAttemptToFix = isTestManagementTestsEnabled &&
|
|
395
|
+
getTestProperties(getTestSuitePath(testFileAbsolutePath, process.cwd()), pickle.name).attemptToFix
|
|
396
|
+
|
|
397
|
+
if (!isAttemptToFix && shouldRunEarlyFlakeDetection() && (isNew || modifiedTestsByPickleId.get(pickle.id))) {
|
|
398
|
+
let testStatuses = efdStatusesByPickleId.get(pickle.id)
|
|
391
399
|
if (testStatuses) {
|
|
392
400
|
testStatuses.push(status)
|
|
393
401
|
} else {
|
|
394
402
|
testStatuses = [status]
|
|
395
|
-
|
|
403
|
+
efdStatusesByPickleId.set(pickle.id, testStatuses)
|
|
396
404
|
}
|
|
397
405
|
let efdRetryCount = efdRetryCountByPickleId.get(pickle.id)
|
|
398
406
|
if (efdRetryCount === undefined) {
|
|
@@ -404,11 +412,8 @@ function handleParallelTestCaseFinished (pickle, worstTestStepResult, usesNumeri
|
|
|
404
412
|
efdSlowAbortedPickleIds.add(pickle.id)
|
|
405
413
|
}
|
|
406
414
|
}
|
|
407
|
-
maybeRecordFinalParallelEfdStatus({ pickleId: pickle.id, testFileAbsolutePath
|
|
408
|
-
} else if (
|
|
409
|
-
isTestManagementTestsEnabled &&
|
|
410
|
-
getTestProperties(getTestSuitePath(testFileAbsolutePath, process.cwd()), pickle.name).attemptToFix
|
|
411
|
-
) {
|
|
415
|
+
maybeRecordFinalParallelEfdStatus({ pickleId: pickle.id, testFileAbsolutePath })
|
|
416
|
+
} else if (isAttemptToFix) {
|
|
412
417
|
const testFullname = `${pickle.uri}:${pickle.name}`
|
|
413
418
|
let testStatuses = attemptToFixTestsByTestFullname.get(testFullname)
|
|
414
419
|
if (testStatuses) {
|
|
@@ -495,8 +500,14 @@ function finishParallelSuiteIfDone (testFileAbsolutePath) {
|
|
|
495
500
|
const finished = pickleResultByFile[testFileAbsolutePath]
|
|
496
501
|
const expectedPickles = pickleByFile[testFileAbsolutePath]
|
|
497
502
|
|
|
498
|
-
if (
|
|
499
|
-
|
|
503
|
+
if (
|
|
504
|
+
!finished ||
|
|
505
|
+
!expectedPickles ||
|
|
506
|
+
finished.length !== expectedPickles.length ||
|
|
507
|
+
finishedParallelSuites.has(testFileAbsolutePath)
|
|
508
|
+
) {
|
|
509
|
+
return
|
|
510
|
+
}
|
|
500
511
|
|
|
501
512
|
finishedParallelSuites.add(testFileAbsolutePath)
|
|
502
513
|
testSuiteFinishCh.publish({
|
|
@@ -505,29 +516,30 @@ function finishParallelSuiteIfDone (testFileAbsolutePath) {
|
|
|
505
516
|
})
|
|
506
517
|
}
|
|
507
518
|
|
|
508
|
-
function maybeRecordFinalParallelEfdStatus ({ pickleId, testFileAbsolutePath
|
|
519
|
+
function maybeRecordFinalParallelEfdStatus ({ pickleId, testFileAbsolutePath }) {
|
|
509
520
|
const efdRetryCount = efdRetryCountByPickleId.get(pickleId)
|
|
510
|
-
const testStatuses =
|
|
521
|
+
const testStatuses = efdStatusesByPickleId.get(pickleId)
|
|
511
522
|
const finished = pickleResultByFile[testFileAbsolutePath]
|
|
512
523
|
|
|
513
|
-
if (efdRetryCount === undefined || !testStatuses || !finished) return
|
|
514
|
-
if (testStatuses.length !== efdRetryCount + 1) return
|
|
524
|
+
if (efdRetryCount === undefined || !testStatuses || !finished || testStatuses.length !== efdRetryCount + 1) return
|
|
515
525
|
|
|
516
|
-
finished.push(getTestStatusFromRetries(testStatuses))
|
|
517
|
-
|
|
526
|
+
finished.push(efdRetryCount === 0 ? testStatuses[0] : getTestStatusFromRetries(testStatuses))
|
|
527
|
+
efdStatusesByPickleId.delete(pickleId)
|
|
518
528
|
finishParallelSuiteIfDone(testFileAbsolutePath)
|
|
519
529
|
}
|
|
520
530
|
|
|
521
531
|
function handleEfdRetryCountMessage (message) {
|
|
522
|
-
const { pickleId, retryCount,
|
|
532
|
+
const { pickleId, retryCount, isModified, testFileAbsolutePath } = message
|
|
523
533
|
|
|
524
|
-
if (!pickleId ||
|
|
534
|
+
if (!pickleId || !testFileAbsolutePath) return
|
|
535
|
+
|
|
536
|
+
if (isModified) modifiedTestsByPickleId.set(pickleId, true)
|
|
537
|
+
if (typeof retryCount !== 'number') return
|
|
525
538
|
|
|
526
539
|
efdRetryCountByPickleId.set(pickleId, retryCount)
|
|
527
540
|
maybeRecordFinalParallelEfdStatus({
|
|
528
541
|
pickleId,
|
|
529
542
|
testFileAbsolutePath,
|
|
530
|
-
testFullname: `${testFileAbsolutePath}:${testName}`,
|
|
531
543
|
})
|
|
532
544
|
}
|
|
533
545
|
|
|
@@ -733,9 +745,10 @@ function publishRetriedAttempt (runner, state) {
|
|
|
733
745
|
const nextAttempt = currentAttempt + 1
|
|
734
746
|
const failedAttemptCtx = numAttemptToCtx.get(currentAttempt)
|
|
735
747
|
const isFirstAttempt = currentAttempt === 0
|
|
736
|
-
const isAtrRetry = !isFirstAttempt &&
|
|
748
|
+
const isAtrRetry = !isFirstAttempt && state.hasAutomaticRetries
|
|
737
749
|
|
|
738
750
|
if (isFirstAttempt && isDynamicAtrEnabled && isFlakyTestRetriesEnabled) {
|
|
751
|
+
state.hasAutomaticRetries = true
|
|
739
752
|
state.dynamicAtrRetryCount = getDynamicAtrRetryCount(
|
|
740
753
|
performance.now() - state.executionStart,
|
|
741
754
|
earlyFlakeDetectionRetryPolicy,
|
|
@@ -747,7 +760,7 @@ function publishRetriedAttempt (runner, state) {
|
|
|
747
760
|
}
|
|
748
761
|
|
|
749
762
|
// ATR: record this attempt as failed so when run().finally runs (after retry) we have all statuses
|
|
750
|
-
if (
|
|
763
|
+
if (state.hasAutomaticRetries) {
|
|
751
764
|
const nameForKey = getCucumberTestName(runner.pickle.name, currentAttempt > 0)
|
|
752
765
|
const atrKey = `${runner.pickle.uri}:${nameForKey}`
|
|
753
766
|
if (atrStatusesByScenarioKey.has(atrKey)) {
|
|
@@ -823,7 +836,7 @@ function wrapRun (pl, isLatestVersion, version) {
|
|
|
823
836
|
}
|
|
824
837
|
const ctx = testStartPayload
|
|
825
838
|
const promises = {}
|
|
826
|
-
const state = { numAttempt: 0, promises, testStartPayload }
|
|
839
|
+
const state = { numAttempt: 0, promises, testStartPayload, hasAutomaticRetries }
|
|
827
840
|
numAttemptToCtx.set(state.numAttempt, ctx)
|
|
828
841
|
runnerToRetryState.set(this, state)
|
|
829
842
|
if (isTestManagementTestsEnabled && getTestProperties(testSuitePath, this.pickle.name).attemptToFix) {
|
|
@@ -912,12 +925,13 @@ function wrapRun (pl, isLatestVersion, version) {
|
|
|
912
925
|
isNew = newTestPickleIds.has(this.pickle.id)
|
|
913
926
|
}
|
|
914
927
|
|
|
915
|
-
if (isNew || isModified) {
|
|
928
|
+
if (!isAttemptToFix && (isNew || isModified)) {
|
|
916
929
|
isEfdRetry = numRetries > 0
|
|
917
930
|
}
|
|
918
931
|
|
|
919
932
|
if (
|
|
920
933
|
shouldRunEarlyFlakeDetection() &&
|
|
934
|
+
!isAttemptToFix &&
|
|
921
935
|
status !== 'skip' &&
|
|
922
936
|
(isNew || isModified) &&
|
|
923
937
|
!isEfdRetry &&
|
|
@@ -952,7 +966,7 @@ function wrapRun (pl, isLatestVersion, version) {
|
|
|
952
966
|
|
|
953
967
|
// ATR: accumulate statuses by stable scenario key (uri:name) so retries are grouped.
|
|
954
968
|
// Cucumber appends " (attempt N)" or " (attempt N, retried)" to the scenario name; normalize for keying.
|
|
955
|
-
if (
|
|
969
|
+
if (state.hasAutomaticRetries && !isAttemptToFix && !isEfdRetry && numTestRetries > 0) {
|
|
956
970
|
const atrKey = `${this.pickle.uri}:${testName}`
|
|
957
971
|
if (atrStatusesByScenarioKey.has(atrKey)) {
|
|
958
972
|
atrStatusesByScenarioKey.get(atrKey).push(status)
|
|
@@ -1001,7 +1015,7 @@ function wrapRun (pl, isLatestVersion, version) {
|
|
|
1001
1015
|
|
|
1002
1016
|
// Notice that ATR is handled using cucumber native retries features.
|
|
1003
1017
|
// Therefore, if we reach this point, we are certain that it's the last ATR execution
|
|
1004
|
-
const isLastAtrRetry =
|
|
1018
|
+
const isLastAtrRetry = state.hasAutomaticRetries && !isAttemptToFix && !isEfdRetry &&
|
|
1005
1019
|
numTestRetries > 0 && this.maxAttempts > 1
|
|
1006
1020
|
|
|
1007
1021
|
const statuses = lastStatusByPickleId.get(this.pickle.id)
|
|
@@ -1030,8 +1044,8 @@ function wrapRun (pl, isLatestVersion, version) {
|
|
|
1030
1044
|
error,
|
|
1031
1045
|
isNew,
|
|
1032
1046
|
isEfdRetry,
|
|
1033
|
-
isFlakyRetry: state.numAttempt > 0 &&
|
|
1034
|
-
isExternalRetry: state.numAttempt > 0 && !
|
|
1047
|
+
isFlakyRetry: state.numAttempt > 0 && state.hasAutomaticRetries,
|
|
1048
|
+
isExternalRetry: state.numAttempt > 0 && !state.hasAutomaticRetries,
|
|
1035
1049
|
isAttemptToFix,
|
|
1036
1050
|
isAttemptToFixRetry,
|
|
1037
1051
|
hasFailedAllRetries,
|
|
@@ -1252,7 +1266,9 @@ function getWrappedStart (start, frameworkVersion, isParallel = false, isCoordin
|
|
|
1252
1266
|
const processArgv = process.argv.slice(2).join(' ')
|
|
1253
1267
|
const command = getEnvironmentVariable('npm_lifecycle_script') || `cucumber-js ${processArgv}`
|
|
1254
1268
|
|
|
1255
|
-
|
|
1269
|
+
const originalRetryCount = options.retry
|
|
1270
|
+
hasAutomaticRetries = isFlakyTestRetriesEnabled && !options.retry && numTestRetries > 0
|
|
1271
|
+
if (hasAutomaticRetries) {
|
|
1256
1272
|
options.retry = numTestRetries
|
|
1257
1273
|
}
|
|
1258
1274
|
|
|
@@ -1265,7 +1281,8 @@ function getWrappedStart (start, frameworkVersion, isParallel = false, isCoordin
|
|
|
1265
1281
|
efdRetryCountByPickleId.clear()
|
|
1266
1282
|
efdSlowAbortedPickleIds.clear()
|
|
1267
1283
|
finishedParallelSuites.clear()
|
|
1268
|
-
|
|
1284
|
+
efdStatusesByPickleId.clear()
|
|
1285
|
+
modifiedTestsByPickleId.clear()
|
|
1269
1286
|
sessionStartCh.publish({ command, frameworkVersion })
|
|
1270
1287
|
|
|
1271
1288
|
if (!errorSkippableRequest && skippedSuites.length) {
|
|
@@ -1293,6 +1310,8 @@ function getWrappedStart (start, frameworkVersion, isParallel = false, isCoordin
|
|
|
1293
1310
|
log.error('Cucumber test session finalization error: %s', finalizationError)
|
|
1294
1311
|
}
|
|
1295
1312
|
throw error
|
|
1313
|
+
} finally {
|
|
1314
|
+
if (hasAutomaticRetries) options.retry = originalRetryCount
|
|
1296
1315
|
}
|
|
1297
1316
|
const success = satisfies(frameworkVersion, '>=13.1.0') ? result.success : result
|
|
1298
1317
|
|
|
@@ -1478,10 +1497,15 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
|
|
|
1478
1497
|
}
|
|
1479
1498
|
}
|
|
1480
1499
|
const originalRetry = this.options.retry
|
|
1481
|
-
const
|
|
1500
|
+
const isEfdTest = !isAttemptToFix && shouldRunEarlyFlakeDetection() && (isNew || isModified)
|
|
1501
|
+
const isManagedRetry = isAttemptToFix || isEfdTest
|
|
1482
1502
|
if (isManagedRetry) {
|
|
1483
1503
|
this.options.retry = 0
|
|
1484
1504
|
}
|
|
1505
|
+
if (isWorker && isModified && isEfdTest) {
|
|
1506
|
+
// The coordinator must recognize modified EFD tests before their first result arrives.
|
|
1507
|
+
publishWorkerEfdRetryCount(pickle, undefined, true)
|
|
1508
|
+
}
|
|
1485
1509
|
// TODO: for >=11 we could use `runTestCaseResult` instead of accumulating results in `lastStatusByPickleId`
|
|
1486
1510
|
const firstExecutionStart = performance.now()
|
|
1487
1511
|
let runTestCaseResult = await runTestCaseFunction.apply(this, arguments)
|
|
@@ -1493,7 +1517,7 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
|
|
|
1493
1517
|
const testStatuses = lastStatusByPickleId.get(pickle.id)
|
|
1494
1518
|
const lastTestStatus = testStatuses.at(-1)
|
|
1495
1519
|
|
|
1496
|
-
//
|
|
1520
|
+
// Attempt to Fix owns retries for managed tests, including modified tests.
|
|
1497
1521
|
if (isAttemptToFix && lastTestStatus !== 'skip') {
|
|
1498
1522
|
for (let retryIndex = 0; retryIndex < testManagementAttemptToFixRetries; retryIndex++) {
|
|
1499
1523
|
numRetriesByPickleId.set(pickle.id, retryIndex + 1)
|
|
@@ -1502,8 +1526,8 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
|
|
|
1502
1526
|
}
|
|
1503
1527
|
}
|
|
1504
1528
|
|
|
1505
|
-
//
|
|
1506
|
-
if (
|
|
1529
|
+
// Retry eligible new or modified tests unless their first attempt was skipped.
|
|
1530
|
+
if (isEfdTest && lastTestStatus !== 'skip') {
|
|
1507
1531
|
let efdRetryCount = efdRetryCountByPickleId.get(pickle.id)
|
|
1508
1532
|
if (efdRetryCount === undefined) {
|
|
1509
1533
|
efdRetryCount = getEfdRetryCountForDuration(firstExecutionDurationMs, earlyFlakeDetectionRetryPolicy)
|
|
@@ -1525,7 +1549,7 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
|
|
|
1525
1549
|
let shouldBePassedByEFD = false
|
|
1526
1550
|
let shouldBePassedByTestManagement = false
|
|
1527
1551
|
let shouldBeFailedByAttemptToFix = false
|
|
1528
|
-
if (
|
|
1552
|
+
if (isEfdTest) {
|
|
1529
1553
|
/**
|
|
1530
1554
|
* If Early Flake Detection (EFD) is enabled the logic is as follows:
|
|
1531
1555
|
* - If all attempts for a test are failing, the test has failed and we will let the test process fail.
|
|
@@ -1587,7 +1611,7 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
|
|
|
1587
1611
|
return shouldBePassedByTestManagement
|
|
1588
1612
|
}
|
|
1589
1613
|
|
|
1590
|
-
if (isNewerCucumberVersion &&
|
|
1614
|
+
if (isNewerCucumberVersion && isEfdTest) {
|
|
1591
1615
|
return shouldBePassedByEFD
|
|
1592
1616
|
}
|
|
1593
1617
|
|
|
@@ -72,11 +72,7 @@ function createWrapRequest (name) {
|
|
|
72
72
|
|
|
73
73
|
return function wrapRequest (request) {
|
|
74
74
|
return function (params, options, cb) {
|
|
75
|
-
if (!startCh.hasSubscribers)
|
|
76
|
-
return request.apply(this, arguments)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (!params) return request.apply(this, arguments)
|
|
75
|
+
if (!startCh.hasSubscribers || !params) return request.apply(this, arguments)
|
|
80
76
|
|
|
81
77
|
const ctx = { params }
|
|
82
78
|
return startCh.runStores(ctx, () => {
|
|
@@ -83,10 +83,10 @@ function createWrapAddListener (ch, mappings) {
|
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
function createWrapRemoveListener (
|
|
86
|
+
function createWrapRemoveListener () {
|
|
87
87
|
return function wrapRemoveListener (removeListener) {
|
|
88
88
|
return function (channel, listener) {
|
|
89
|
-
const mapping =
|
|
89
|
+
const mapping = listeners[channel]
|
|
90
90
|
|
|
91
91
|
if (mapping) {
|
|
92
92
|
const wrapper = mapping.get(listener)
|
|
@@ -212,10 +212,10 @@ addHook({ name: 'electron', versions: ['>=37.0.0'] }, electron => {
|
|
|
212
212
|
wrap(ipcRenderer, 'sendToHost', createWrapSend(rendererSendCh))
|
|
213
213
|
|
|
214
214
|
wrap(ipcRenderer, 'addListener', createWrapAddListener(rendererReceiveCh, listeners))
|
|
215
|
-
wrap(ipcRenderer, 'off', createWrapRemoveListener(
|
|
215
|
+
wrap(ipcRenderer, 'off', createWrapRemoveListener())
|
|
216
216
|
wrap(ipcRenderer, 'on', createWrapAddListener(rendererReceiveCh, listeners))
|
|
217
217
|
wrap(ipcRenderer, 'once', createWrapAddListener(rendererReceiveCh, listeners))
|
|
218
|
-
wrap(ipcRenderer, 'removeListener', createWrapRemoveListener(
|
|
218
|
+
wrap(ipcRenderer, 'removeListener', createWrapRemoveListener())
|
|
219
219
|
wrap(ipcRenderer, 'removeAllListeners', createWrapRemoveAllListeners(listeners))
|
|
220
220
|
|
|
221
221
|
ipcRenderer.send('datadog:apm:renderer:patched')
|
|
@@ -223,12 +223,12 @@ addHook({ name: 'electron', versions: ['>=37.0.0'] }, electron => {
|
|
|
223
223
|
wrap(ipcMain, 'addListener', createWrapAddListener(mainReceiveCh, listeners))
|
|
224
224
|
wrap(ipcMain, 'handle', createWrapAddListener(mainHandleCh, handlers))
|
|
225
225
|
wrap(ipcMain, 'handleOnce', createWrapAddListener(mainHandleCh, handlers))
|
|
226
|
-
wrap(ipcMain, 'off', createWrapRemoveListener(
|
|
226
|
+
wrap(ipcMain, 'off', createWrapRemoveListener())
|
|
227
227
|
wrap(ipcMain, 'on', createWrapAddListener(mainReceiveCh, listeners))
|
|
228
228
|
wrap(ipcMain, 'once', createWrapAddListener(mainReceiveCh, listeners))
|
|
229
229
|
wrap(ipcMain, 'removeAllListeners', createWrapRemoveAllListeners(listeners))
|
|
230
230
|
wrap(ipcMain, 'removeHandler', createWrapRemoveAllListeners(handlers))
|
|
231
|
-
wrap(ipcMain, 'removeListener', createWrapRemoveListener(
|
|
231
|
+
wrap(ipcMain, 'removeListener', createWrapRemoveListener())
|
|
232
232
|
|
|
233
233
|
ipcMain.once('datadog:apm:renderer:patched', event => rendererPatchedCh.publish(event))
|
|
234
234
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const shimmer = require('../../datadog-shimmer')
|
|
4
|
+
const { isEmpty } = require('../../dd-trace/src/util')
|
|
4
5
|
const { addHook, channel, createErrorPublisher } = require('./helpers/instrument')
|
|
5
6
|
|
|
6
7
|
const errorChannel = channel('apm:fastify:middleware:error')
|
|
@@ -186,6 +187,17 @@ function onRequest (request, reply, done) {
|
|
|
186
187
|
return done()
|
|
187
188
|
}
|
|
188
189
|
|
|
190
|
+
/**
|
|
191
|
+
* Publishing an empty body would set a WAF address for nothing, so only non-empty bodies are published.
|
|
192
|
+
* Strings, arrays and binary bodies are checked by length to avoid enumerating one key per character or element.
|
|
193
|
+
*
|
|
194
|
+
* @param {unknown} body
|
|
195
|
+
*/
|
|
196
|
+
function hasBody (body) {
|
|
197
|
+
if (typeof body === 'string') return body.length > 0
|
|
198
|
+
return typeof body === 'object' && body !== null && !isEmpty(body)
|
|
199
|
+
}
|
|
200
|
+
|
|
189
201
|
function preHandler (request, reply, done) {
|
|
190
202
|
if (typeof done !== 'function') return
|
|
191
203
|
if (!reply || typeof reply.send !== 'function') return done()
|
|
@@ -194,11 +206,8 @@ function preHandler (request, reply, done) {
|
|
|
194
206
|
const res = getRes(reply)
|
|
195
207
|
const ctx = { req, res }
|
|
196
208
|
|
|
197
|
-
// eslint-disable-next-line no-restricted-syntax -- arbitrary body keys; publishing {} sets a WAF address
|
|
198
|
-
const hasBody = request.body && Object.keys(request.body).length > 0
|
|
199
|
-
|
|
200
209
|
// For multipart/form-data, the body is not available until after preValidation hook
|
|
201
|
-
if (bodyParserReadCh.hasSubscribers &&
|
|
210
|
+
if (bodyParserReadCh.hasSubscribers && !bodyPublished.has(req) && hasBody(request.body)) {
|
|
202
211
|
ctx.abortController = new AbortController()
|
|
203
212
|
ctx.body = request.body
|
|
204
213
|
bodyParserReadCh.publish(ctx)
|
|
@@ -232,9 +232,9 @@ function getMethodParamsRelationByPrefix (prefix) {
|
|
|
232
232
|
return paramsByMethod
|
|
233
233
|
}
|
|
234
234
|
|
|
235
|
-
function createWatchWrapFunction (
|
|
235
|
+
function createWatchWrapFunction () {
|
|
236
236
|
return function wrapFunction (original) {
|
|
237
|
-
const name =
|
|
237
|
+
const name = original.name
|
|
238
238
|
const method = name
|
|
239
239
|
const operation = name
|
|
240
240
|
return function (...args) {
|
|
@@ -126,10 +126,14 @@ function wrapMethod (method) {
|
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
-
|
|
129
|
+
/**
|
|
130
|
+
* @param {Record<string, Function>} obj
|
|
131
|
+
* @param {string[]} methods
|
|
132
|
+
*/
|
|
133
|
+
function massWrap (obj, methods) {
|
|
130
134
|
for (const method of methods) {
|
|
131
135
|
if (typeof obj[method] === 'function') {
|
|
132
|
-
shimmer.wrap(obj, method,
|
|
136
|
+
shimmer.wrap(obj, method, wrapMethod)
|
|
133
137
|
}
|
|
134
138
|
}
|
|
135
139
|
}
|
|
@@ -274,11 +278,11 @@ addHook({ name: '@google-cloud/pubsub', versions: ['>=1.2'] }, (obj) => {
|
|
|
274
278
|
addHook({ name: '@google-cloud/pubsub', versions: ['>=1.2'] }, (obj) => {
|
|
275
279
|
const { PublisherClient, SchemaServiceClient, SubscriberClient } = obj.v1
|
|
276
280
|
|
|
277
|
-
massWrap(PublisherClient.prototype, publisherMethods
|
|
278
|
-
massWrap(SubscriberClient.prototype, subscriberMethods
|
|
281
|
+
massWrap(PublisherClient.prototype, publisherMethods)
|
|
282
|
+
massWrap(SubscriberClient.prototype, subscriberMethods)
|
|
279
283
|
|
|
280
284
|
if (SchemaServiceClient) {
|
|
281
|
-
massWrap(SchemaServiceClient.prototype, schemaServiceMethods
|
|
285
|
+
massWrap(SchemaServiceClient.prototype, schemaServiceMethods)
|
|
282
286
|
}
|
|
283
287
|
|
|
284
288
|
return obj
|
|
@@ -246,6 +246,8 @@ function patch (hasPeer = false) {
|
|
|
246
246
|
return function patch (grpc) {
|
|
247
247
|
const proto = grpc.Client.prototype
|
|
248
248
|
|
|
249
|
+
if (instances.has(proto)) return grpc
|
|
250
|
+
|
|
249
251
|
instances.set(proto, grpc)
|
|
250
252
|
|
|
251
253
|
shimmer.wrap(proto, 'makeBidiStreamRequest', createWrapMakeRequest(types.bidi, hasPeer))
|
|
@@ -4,7 +4,7 @@ const Module = require('module')
|
|
|
4
4
|
const dc = require('dc-polyfill')
|
|
5
5
|
|
|
6
6
|
const log = require('../../../dd-trace/src/log')
|
|
7
|
-
const { loadChannel } = require('./register.js')
|
|
7
|
+
const { activate, loadChannel, registeredBuiltinNames } = require('./register.js')
|
|
8
8
|
const {
|
|
9
9
|
filename,
|
|
10
10
|
getDisabledInstrumentations,
|
|
@@ -17,25 +17,44 @@ const { isRewriteActivationEnabled } = require('./rewriter/targets')
|
|
|
17
17
|
const disabledInstrumentations = getDisabledInstrumentations()
|
|
18
18
|
|
|
19
19
|
// register.js has now set up ritm (require-in-the-middle). In bundled
|
|
20
|
-
// environments (webpack, esbuild), Node.js built-
|
|
21
|
-
// dd-trace
|
|
22
|
-
// before ritm was active
|
|
23
|
-
//
|
|
24
|
-
// Re-requiring them via the real Module.prototype.require ensures ritm applies
|
|
25
|
-
// their instrumentation hooks.
|
|
20
|
+
// environments (webpack, esbuild, Turbopack), Node.js built-ins required by the
|
|
21
|
+
// application or by dd-trace itself may have been loaded and cached by the
|
|
22
|
+
// bundle before ritm was active, bypassing it. Re-requiring them via the real
|
|
23
|
+
// Module.prototype.require lets ritm apply their hooks to the shared exports.
|
|
26
24
|
//
|
|
27
25
|
// In regular Node.js, `module` is an instance of Module. In bundlers, the
|
|
28
26
|
// module wrapper object is a plain object (not a Module instance), so we use
|
|
29
27
|
// that to detect a bundled context and avoid unintended side-effects in
|
|
30
28
|
// normal Node.js (e.g. shimmer-wrapping http before ESM modules load).
|
|
31
29
|
if (!(module instanceof Module)) {
|
|
32
|
-
|
|
30
|
+
patchLoadedBuiltins()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function patchLoadedBuiltins () {
|
|
34
|
+
// A built-in nobody has loaded cannot be cached by the bundle yet, and a later require reaches ritm, so it is
|
|
35
|
+
// skipped. moduleLoadList is undocumented: if it ever disappears, every registered built-in is loaded instead.
|
|
36
|
+
// @ts-expect-error - process.moduleLoadList is not typed
|
|
37
|
+
const { moduleLoadList } = process
|
|
38
|
+
const loadedBuiltins = Array.isArray(moduleLoadList) ? moduleLoadList : undefined
|
|
39
|
+
|
|
40
|
+
let patched = false
|
|
41
|
+
for (const name of registeredBuiltinNames) {
|
|
42
|
+
if (loadedBuiltins?.includes(`NativeModule ${name}`) === false) continue
|
|
43
|
+
|
|
33
44
|
try {
|
|
34
45
|
Module.prototype.require.call(module, name)
|
|
46
|
+
patched = true
|
|
35
47
|
} catch {
|
|
36
48
|
// Built-in not available in this environment, skip
|
|
37
49
|
}
|
|
38
50
|
}
|
|
51
|
+
|
|
52
|
+
if (!patched) return
|
|
53
|
+
try {
|
|
54
|
+
Module.syncBuiltinESMExports()
|
|
55
|
+
} catch (error) {
|
|
56
|
+
log.error('Error synchronizing bundled built-in ESM exports: %s', String(error?.message ?? error), error)
|
|
57
|
+
}
|
|
39
58
|
}
|
|
40
59
|
|
|
41
60
|
const CHANNEL = 'dd-trace:bundler:load'
|
|
@@ -96,7 +115,7 @@ dc.subscribe(CHANNEL, (message) => {
|
|
|
96
115
|
if (disabledInstrumentations.has(name)) return
|
|
97
116
|
|
|
98
117
|
if (payload.activate && isRewriteActivationEnabled(name)) {
|
|
99
|
-
|
|
118
|
+
activate(name, payload.version)
|
|
100
119
|
return
|
|
101
120
|
}
|
|
102
121
|
|
|
@@ -132,8 +132,7 @@ module.exports.checkForPotentialConflicts = function () {
|
|
|
132
132
|
|
|
133
133
|
for (const pathToModule of Object.keys(require.cache)) {
|
|
134
134
|
const { pkg } = extractPackageAndModulePath(pathToModule)
|
|
135
|
-
if (naughties.has(pkg)) continue
|
|
136
|
-
if (!potentialConflicts.has(pkg)) continue
|
|
135
|
+
if (naughties.has(pkg) || !potentialConflicts.has(pkg)) continue
|
|
137
136
|
|
|
138
137
|
warnings.push(() => `Warning: Package '${pkg}' may cause conflicts with dd-trace.`)
|
|
139
138
|
|
|
@@ -97,6 +97,7 @@ module.exports = {
|
|
|
97
97
|
'jest-config': () => require('../jest'),
|
|
98
98
|
'jest-environment-node': () => require('../jest'),
|
|
99
99
|
'jest-environment-jsdom': () => require('../jest'),
|
|
100
|
+
'jest-runner': () => require('../jest'),
|
|
100
101
|
'jest-runtime': () => require('../jest'),
|
|
101
102
|
'jest-worker': () => require('../jest'),
|
|
102
103
|
knex: () => require('../knex'),
|
|
@@ -88,7 +88,7 @@ exports.getHooks = function getHooks (names) {
|
|
|
88
88
|
for (const { module } of rewriterInstrumentations) {
|
|
89
89
|
if (!requested.has(module.name)) continue
|
|
90
90
|
// Fresh objects, including the versions array: callers may adjust a hook
|
|
91
|
-
// for their own registration (the ai
|
|
91
|
+
// for their own registration (the ai and
|
|
92
92
|
// aws-durable-execution-sdk-js plugins set `hook.file = null`), which must
|
|
93
93
|
// not leak into any other call.
|
|
94
94
|
const hook = { name: module.name, versions: [module.versionRange], file: module.filePath }
|
|
@@ -14,6 +14,8 @@ const {
|
|
|
14
14
|
matchesInstrumentation,
|
|
15
15
|
} = require('./instrumentation-utils')
|
|
16
16
|
const rewriter = require('./rewriter')
|
|
17
|
+
const { getActivationSetup } = require('./rewriter/instrumentation-registry')
|
|
18
|
+
const { isNodeBuiltinModuleName } = require('./shared-utils')
|
|
17
19
|
|
|
18
20
|
const DD_TRACE_DEBUG = getValueFromEnvSources('DD_TRACE_DEBUG')
|
|
19
21
|
|
|
@@ -58,6 +60,44 @@ const instrumentedIntegrationsSuccess = new Map()
|
|
|
58
60
|
const alreadyLoggedIncompatibleIntegrations = new Set()
|
|
59
61
|
/** @type {Set<string>} */
|
|
60
62
|
const compatibleOrchestrionTargets = new Set()
|
|
63
|
+
/** @type {Set<(activation: import('./rewriter/instrumentation-registry').Activation) => void>} */
|
|
64
|
+
const blockedActivationSetups = new Set()
|
|
65
|
+
/**
|
|
66
|
+
* Node.js built-ins that passed the disabled and serverless checks and received a hook.
|
|
67
|
+
* @type {string[]}
|
|
68
|
+
*/
|
|
69
|
+
const registeredBuiltinNames = []
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @param {string} moduleName
|
|
73
|
+
* @param {string} [version]
|
|
74
|
+
*/
|
|
75
|
+
function activate (moduleName, version) {
|
|
76
|
+
const setup = getActivationSetup(moduleName)
|
|
77
|
+
if (setup) {
|
|
78
|
+
if (blockedActivationSetups.has(setup)) return
|
|
79
|
+
// Block re-entrant activation until setup completes, and never retry a failed setup.
|
|
80
|
+
blockedActivationSetups.add(setup)
|
|
81
|
+
try {
|
|
82
|
+
setup({ moduleName, version })
|
|
83
|
+
blockedActivationSetups.delete(setup)
|
|
84
|
+
} catch (error) {
|
|
85
|
+
const message = String(error?.message ?? error)
|
|
86
|
+
log.error('Error during activation setup of %s: %s', moduleName, message, error)
|
|
87
|
+
telemetry('error', [
|
|
88
|
+
`error_type:${error?.constructor?.name ?? typeof error}`,
|
|
89
|
+
`integration:${moduleName}`,
|
|
90
|
+
`integration_version:${version ?? 'unknown'}`,
|
|
91
|
+
], {
|
|
92
|
+
result: 'error',
|
|
93
|
+
result_class: 'internal_error',
|
|
94
|
+
result_reason: `Error during activation of ${moduleName}: ${message}`,
|
|
95
|
+
})
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
loadChannel.publish({ name: moduleName })
|
|
100
|
+
}
|
|
61
101
|
|
|
62
102
|
orchestrionLoadChannel.subscribe(({ moduleName, version, result }) => {
|
|
63
103
|
const nameVersion = `${moduleName}@${version}`
|
|
@@ -72,7 +112,7 @@ orchestrionLoadChannel.subscribe(({ moduleName, version, result }) => {
|
|
|
72
112
|
if (result === 'matched' || result === 'rewritten') {
|
|
73
113
|
compatibleOrchestrionTargets.add(nameVersion)
|
|
74
114
|
instrumentedIntegrationsSuccess.set(nameVersion, true)
|
|
75
|
-
if (result === 'rewritten')
|
|
115
|
+
if (result === 'rewritten') activate(moduleName, version)
|
|
76
116
|
}
|
|
77
117
|
})
|
|
78
118
|
|
|
@@ -90,6 +130,8 @@ for (const name of names) {
|
|
|
90
130
|
hook = hook.fn
|
|
91
131
|
}
|
|
92
132
|
|
|
133
|
+
if (isNodeBuiltinModuleName(name)) registeredBuiltinNames.push(name)
|
|
134
|
+
|
|
93
135
|
Hook([name], hookOptions, (moduleExports, moduleName, moduleBaseDir, moduleVersion, isIitm) => {
|
|
94
136
|
// All loaded versions are first expected to fail instrumentation.
|
|
95
137
|
if (!instrumentedIntegrationsSuccess.has(`${name}@${moduleVersion}`)) {
|
|
@@ -176,7 +218,9 @@ function logAbortedIntegrations () {
|
|
|
176
218
|
}
|
|
177
219
|
|
|
178
220
|
module.exports = {
|
|
221
|
+
activate,
|
|
179
222
|
filename,
|
|
180
223
|
pathSepExpr,
|
|
181
224
|
loadChannel,
|
|
225
|
+
registeredBuiltinNames,
|
|
182
226
|
}
|
|
@@ -188,6 +188,7 @@ function createMatcher (dcModule) {
|
|
|
188
188
|
waitForAsyncEnd,
|
|
189
189
|
} = require('./transforms')
|
|
190
190
|
const {
|
|
191
|
+
postgresQueryAcquire,
|
|
191
192
|
postgresQueryHandlers,
|
|
192
193
|
postgresQueryLifecycle,
|
|
193
194
|
postgresQueryPreparation,
|
|
@@ -204,6 +205,7 @@ function createMatcher (dcModule) {
|
|
|
204
205
|
matcher.addTransform('configureGraphqlJitRuntime', configureGraphqlJitRuntime)
|
|
205
206
|
matcher.addTransform('configureMercuriusRequest', configureMercuriusRequest)
|
|
206
207
|
matcher.addTransform('publishDurableOrchestrationFailure', publishDurableOrchestrationFailure)
|
|
208
|
+
matcher.addTransform('postgresQueryAcquire', postgresQueryAcquire)
|
|
207
209
|
matcher.addTransform('postgresQueryHandlers', postgresQueryHandlers)
|
|
208
210
|
matcher.addTransform('postgresQueryLifecycle', postgresQueryLifecycle)
|
|
209
211
|
matcher.addTransform('postgresQueryPreparation', postgresQueryPreparation)
|