dd-trace 6.7.0 → 6.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (272) hide show
  1. package/ci/diagnose.js +27 -25
  2. package/ci/init.js +6 -6
  3. package/ci/runbook.md +114 -20
  4. package/ci/test-optimization-validation/approval-artifacts.js +36 -3
  5. package/ci/test-optimization-validation/approval.js +78 -22
  6. package/ci/test-optimization-validation/blocker-category.js +24 -0
  7. package/ci/test-optimization-validation/ci-discovery.js +23 -17
  8. package/ci/test-optimization-validation/ci-package-scripts.js +34 -6
  9. package/ci/test-optimization-validation/ci-remediation.js +1 -1
  10. package/ci/test-optimization-validation/cli.js +280 -52
  11. package/ci/test-optimization-validation/command-blocker.js +159 -20
  12. package/ci/test-optimization-validation/command-output-policy.js +14 -8
  13. package/ci/test-optimization-validation/command-runner.js +9 -7
  14. package/ci/test-optimization-validation/environment.js +4 -2
  15. package/ci/test-optimization-validation/executable.js +15 -13
  16. package/ci/test-optimization-validation/execution-lock.js +70 -0
  17. package/ci/test-optimization-validation/framework-adapters/cucumber-validation.json +3 -0
  18. package/ci/test-optimization-validation/framework-adapters/cucumber.js +657 -5
  19. package/ci/test-optimization-validation/framework-adapters/playwright.js +5 -5
  20. package/ci/test-optimization-validation/framework-adapters/vitest.js +517 -0
  21. package/ci/test-optimization-validation/generated-files.js +47 -37
  22. package/ci/test-optimization-validation/generated-test-contract.js +9 -7
  23. package/ci/test-optimization-validation/generated-verifier.js +24 -17
  24. package/ci/test-optimization-validation/literal-glob.js +52 -0
  25. package/ci/test-optimization-validation/manifest-loader.js +3 -0
  26. package/ci/test-optimization-validation/manifest-scaffold.js +301 -52
  27. package/ci/test-optimization-validation/manifest-schema.js +80 -17
  28. package/ci/test-optimization-validation/offline-fixtures.js +13 -4
  29. package/ci/test-optimization-validation/offline-output.js +6 -6
  30. package/ci/test-optimization-validation/package-check.js +94 -0
  31. package/ci/test-optimization-validation/plan-writer.js +183 -10
  32. package/ci/test-optimization-validation/preflight-runner.js +110 -31
  33. package/ci/test-optimization-validation/project-build-artifact.js +31 -0
  34. package/ci/test-optimization-validation/redaction.js +18 -27
  35. package/ci/test-optimization-validation/report-writer.js +182 -48
  36. package/ci/test-optimization-validation/result-semantics.js +20 -3
  37. package/ci/test-optimization-validation/runner-command.js +41 -21
  38. package/ci/test-optimization-validation/runner-contract.js +79 -43
  39. package/ci/test-optimization-validation/safe-files.js +4 -4
  40. package/ci/test-optimization-validation/scenarios/basic-reporting.js +60 -6
  41. package/ci/test-optimization-validation/scenarios/ci-wiring.js +141 -35
  42. package/ci/test-optimization-validation/scenarios/helpers.js +30 -6
  43. package/ci/test-optimization-validation/scenarios/test-management.js +2 -2
  44. package/ci/test-optimization-validation/static-diagnosis.js +10 -3
  45. package/ci/test-optimization-validation/test-output.js +2 -1
  46. package/ci/test-optimization-validation/validation-blocker.js +21 -0
  47. package/ci/vitest-no-worker-init-setup.mjs +377 -84
  48. package/index.d.ts +175 -14
  49. package/init.js +1 -17
  50. package/initialize.mjs +11 -0
  51. package/loader-hook.mjs +6 -4
  52. package/package.json +12 -4
  53. package/packages/datadog-core/src/utils/src/kebabcase.js +8 -2
  54. package/packages/datadog-core/src/utils/src/parse-tags.js +0 -1
  55. package/packages/datadog-esbuild/index.js +4 -2
  56. package/packages/datadog-esbuild/src/utils.js +17 -2
  57. package/packages/datadog-instrumentations/src/ai.js +61 -34
  58. package/packages/datadog-instrumentations/src/bluebird.js +6 -6
  59. package/packages/datadog-instrumentations/src/claude-agent-sdk.js +85 -9
  60. package/packages/datadog-instrumentations/src/cucumber.js +131 -51
  61. package/packages/datadog-instrumentations/src/dns.js +21 -2
  62. package/packages/datadog-instrumentations/src/electron.js +1 -0
  63. package/packages/datadog-instrumentations/src/fastify.js +3 -1
  64. package/packages/datadog-instrumentations/src/grpc/client.js +4 -2
  65. package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +3 -1
  66. package/packages/datadog-instrumentations/src/helpers/extract-package-and-module-path.js +10 -0
  67. package/packages/datadog-instrumentations/src/helpers/hook.js +19 -2
  68. package/packages/datadog-instrumentations/src/helpers/hooks.js +4 -0
  69. package/packages/datadog-instrumentations/src/helpers/register.js +9 -3
  70. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +34 -9
  71. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
  72. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +175 -0
  73. package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +20 -2
  74. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.js +32 -0
  75. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +61 -0
  76. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +50 -18
  77. package/packages/datadog-instrumentations/src/helpers/router-helper.js +38 -7
  78. package/packages/datadog-instrumentations/src/helpers/shared-utils.js +39 -0
  79. package/packages/datadog-instrumentations/src/http/client.js +1 -3
  80. package/packages/datadog-instrumentations/src/jest/coverage-backfill.js +1 -1
  81. package/packages/datadog-instrumentations/src/jest.js +986 -246
  82. package/packages/datadog-instrumentations/src/mocha/common.js +1 -3
  83. package/packages/datadog-instrumentations/src/mocha/main.js +109 -51
  84. package/packages/datadog-instrumentations/src/mocha/utils.js +120 -32
  85. package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +44 -0
  86. package/packages/datadog-instrumentations/src/mocha/worker.js +441 -43
  87. package/packages/datadog-instrumentations/src/moleculer/client.js +1 -1
  88. package/packages/datadog-instrumentations/src/mysql.js +1 -1
  89. package/packages/datadog-instrumentations/src/next.js +155 -23
  90. package/packages/datadog-instrumentations/src/openai.js +14 -0
  91. package/packages/datadog-instrumentations/src/pg.js +180 -6
  92. package/packages/datadog-instrumentations/src/playwright.js +176 -81
  93. package/packages/datadog-instrumentations/src/promise-js.js +4 -4
  94. package/packages/datadog-instrumentations/src/promise.js +3 -3
  95. package/packages/datadog-instrumentations/src/rhea.js +1 -1
  96. package/packages/datadog-instrumentations/src/router.js +195 -19
  97. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +567 -65
  98. package/packages/datadog-instrumentations/src/vitest-main.js +438 -54
  99. package/packages/datadog-instrumentations/src/vitest-util.js +42 -7
  100. package/packages/datadog-instrumentations/src/vitest-worker.js +234 -17
  101. package/packages/datadog-instrumentations/src/webdriverio.js +1090 -0
  102. package/packages/datadog-instrumentations/src/when.js +3 -3
  103. package/packages/datadog-instrumentations/src/ws.js +5 -1
  104. package/packages/datadog-plugin-aerospike/src/index.js +1 -3
  105. package/packages/datadog-plugin-amqplib/src/client.js +1 -1
  106. package/packages/datadog-plugin-amqplib/src/producer.js +1 -1
  107. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +59 -48
  108. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +2 -2
  109. package/packages/datadog-plugin-aws-sdk/src/services/stepfunctions.js +2 -2
  110. package/packages/datadog-plugin-azure-event-hubs/src/producer.js +1 -3
  111. package/packages/datadog-plugin-azure-service-bus/src/producer.js +5 -3
  112. package/packages/datadog-plugin-bullmq/src/producer.js +3 -3
  113. package/packages/datadog-plugin-child_process/src/index.js +3 -3
  114. package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +6 -11
  115. package/packages/datadog-plugin-cucumber/src/index.js +5 -1
  116. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +50 -46
  117. package/packages/datadog-plugin-cypress/src/source-map-utils.js +1 -1
  118. package/packages/datadog-plugin-cypress/src/support.js +7 -4
  119. package/packages/datadog-plugin-electron/src/net.js +10 -4
  120. package/packages/datadog-plugin-fastify/src/tracing.js +1 -1
  121. package/packages/datadog-plugin-fs/src/index.js +0 -4
  122. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +14 -7
  123. package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +19 -10
  124. package/packages/datadog-plugin-graphql/src/execute.js +1 -0
  125. package/packages/datadog-plugin-graphql/src/tools/signature.js +1 -1
  126. package/packages/datadog-plugin-http/src/client.js +3 -2
  127. package/packages/datadog-plugin-http2/src/client.js +1 -1
  128. package/packages/datadog-plugin-jest/src/index.js +3 -21
  129. package/packages/datadog-plugin-jest/src/util.js +4 -1
  130. package/packages/datadog-plugin-kafkajs/src/producer.js +2 -1
  131. package/packages/datadog-plugin-langchain/src/tokens.js +2 -2
  132. package/packages/datadog-plugin-mocha/src/index.js +342 -6
  133. package/packages/datadog-plugin-next/src/index.js +57 -9
  134. package/packages/datadog-plugin-openai/src/tracing.js +4 -0
  135. package/packages/datadog-plugin-pg/src/index.js +65 -1
  136. package/packages/datadog-plugin-playwright/src/index.js +1 -1
  137. package/packages/datadog-plugin-prisma/src/index.js +5 -1
  138. package/packages/datadog-plugin-rhea/src/consumer.js +16 -13
  139. package/packages/datadog-plugin-rhea/src/producer.js +1 -1
  140. package/packages/datadog-plugin-undici/src/index.js +2 -1
  141. package/packages/datadog-plugin-vitest/src/index.js +179 -18
  142. package/packages/datadog-shimmer/src/shimmer.js +6 -2
  143. package/packages/datadog-webpack/index.js +2 -2
  144. package/packages/dd-trace/src/aiguard/sdk.js +3 -0
  145. package/packages/dd-trace/src/appsec/{blocking.js → blocking/index.js} +26 -6
  146. package/packages/dd-trace/src/appsec/downstream_requests.js +1 -1
  147. package/packages/dd-trace/src/appsec/handlers/auth.js +62 -0
  148. package/packages/dd-trace/src/appsec/handlers/http-request.js +215 -0
  149. package/packages/dd-trace/src/appsec/handlers/http-response.js +78 -0
  150. package/packages/dd-trace/src/appsec/handlers/http-shared.js +24 -0
  151. package/packages/dd-trace/src/appsec/handlers/payments.js +104 -0
  152. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-base-analyzer.js +1 -1
  153. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-password-analyzer.js +1 -1
  154. package/packages/dd-trace/src/appsec/iast/analyzers/hsts-header-missing-analyzer.js +1 -1
  155. package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +1 -1
  156. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +1 -3
  157. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +2 -2
  158. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +2 -2
  159. package/packages/dd-trace/src/appsec/index.js +48 -476
  160. package/packages/dd-trace/src/appsec/sdk/track_event.js +10 -1
  161. package/packages/dd-trace/src/appsec/waf/waf_manager.js +1 -1
  162. package/packages/dd-trace/src/ci-visibility/coverage-report-discovery.js +46 -14
  163. package/packages/dd-trace/src/ci-visibility/efd-retry-policy.js +89 -0
  164. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/sink.js +7 -6
  165. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +29 -15
  166. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +19 -1
  167. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +25 -1
  168. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/webdriverio.js +26 -0
  169. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +9 -1
  170. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +22 -7
  171. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +9 -14
  172. package/packages/dd-trace/src/ci-visibility/requests/request.js +3 -3
  173. package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +21 -5
  174. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +2 -2
  175. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +5 -1
  176. package/packages/dd-trace/src/config/defaults.js +14 -6
  177. package/packages/dd-trace/src/config/generated-config-types.d.ts +4 -2
  178. package/packages/dd-trace/src/config/helper.js +2 -0
  179. package/packages/dd-trace/src/config/index.js +31 -5
  180. package/packages/dd-trace/src/config/parsers.js +19 -1
  181. package/packages/dd-trace/src/config/remote_config.js +1 -1
  182. package/packages/dd-trace/src/config/supported-configurations.json +13 -6
  183. package/packages/dd-trace/src/datastreams/encoding.js +3 -4
  184. package/packages/dd-trace/src/debugger/devtools_client/log.js +2 -1
  185. package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +1 -4
  186. package/packages/dd-trace/src/debugger/devtools_client/send.js +1 -2
  187. package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +2 -2
  188. package/packages/dd-trace/src/debugger/devtools_client/snapshot/redaction.js +16 -2
  189. package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -1
  190. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +0 -1
  191. package/packages/dd-trace/src/encode/tags-processors.js +2 -2
  192. package/packages/dd-trace/src/exporters/common/url.js +3 -5
  193. package/packages/dd-trace/src/exporters/electron/index.js +4 -1
  194. package/packages/dd-trace/src/external-logger/src/index.js +1 -2
  195. package/packages/dd-trace/src/flare/index.js +2 -2
  196. package/packages/dd-trace/src/guardrails/apply-pm2-env.js +16 -0
  197. package/packages/dd-trace/src/guardrails/log.js +1 -10
  198. package/packages/dd-trace/src/id.js +1 -1
  199. package/packages/dd-trace/src/llmobs/constants/tags.js +1 -1
  200. package/packages/dd-trace/src/llmobs/eval-metric.js +249 -0
  201. package/packages/dd-trace/src/llmobs/experiments/client.js +4 -2
  202. package/packages/dd-trace/src/llmobs/experiments/dataset.js +66 -14
  203. package/packages/dd-trace/src/llmobs/experiments/experiment.js +289 -95
  204. package/packages/dd-trace/src/llmobs/experiments/index.js +66 -19
  205. package/packages/dd-trace/src/llmobs/experiments/noop.js +95 -10
  206. package/packages/dd-trace/src/llmobs/experiments/result.js +13 -2
  207. package/packages/dd-trace/src/llmobs/experiments/util.js +220 -0
  208. package/packages/dd-trace/src/llmobs/noop.js +2 -0
  209. package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +1 -1
  210. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +1 -1
  211. package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +1 -1
  212. package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +5 -5
  213. package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/index.js +1 -1
  214. package/packages/dd-trace/src/llmobs/plugins/genai/index.js +1 -1
  215. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js +1 -1
  216. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +1 -1
  217. package/packages/dd-trace/src/llmobs/sdk.js +152 -78
  218. package/packages/dd-trace/src/llmobs/span_processor.js +9 -7
  219. package/packages/dd-trace/src/llmobs/tagger.js +1 -2
  220. package/packages/dd-trace/src/llmobs/telemetry.js +18 -0
  221. package/packages/dd-trace/src/llmobs/writers/base.js +3 -0
  222. package/packages/dd-trace/src/llmobs/writers/spans.js +12 -6
  223. package/packages/dd-trace/src/llmobs/writers/util.js +2 -0
  224. package/packages/dd-trace/src/log/channels.js +1 -9
  225. package/packages/dd-trace/src/log/levels.js +12 -0
  226. package/packages/dd-trace/src/noop/proxy.js +2 -2
  227. package/packages/dd-trace/src/openfeature/writers/exposures.js +1 -1
  228. package/packages/dd-trace/src/openfeature/writers/util.js +1 -1
  229. package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +1 -1
  230. package/packages/dd-trace/src/opentelemetry/metrics/meter.js +8 -4
  231. package/packages/dd-trace/src/opentelemetry/metrics/otlp_transformer.js +1 -1
  232. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +3 -2
  233. package/packages/dd-trace/src/opentelemetry/span.js +4 -0
  234. package/packages/dd-trace/src/opentracing/span.js +1 -1
  235. package/packages/dd-trace/src/opentracing/span_context.js +1 -1
  236. package/packages/dd-trace/src/plugins/ci_plugin.js +124 -23
  237. package/packages/dd-trace/src/plugins/index.js +2 -0
  238. package/packages/dd-trace/src/plugins/plugin.js +1 -1
  239. package/packages/dd-trace/src/plugins/tracing.js +1 -0
  240. package/packages/dd-trace/src/plugins/util/ci.js +13 -6
  241. package/packages/dd-trace/src/plugins/util/git.js +7 -5
  242. package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +3 -3
  243. package/packages/dd-trace/src/plugins/util/ip_extractor.js +1 -1
  244. package/packages/dd-trace/src/plugins/util/llm.js +2 -2
  245. package/packages/dd-trace/src/plugins/util/stacktrace.js +3 -0
  246. package/packages/dd-trace/src/plugins/util/tags.js +2 -0
  247. package/packages/dd-trace/src/plugins/util/test.js +356 -86
  248. package/packages/dd-trace/src/plugins/util/url.js +5 -5
  249. package/packages/dd-trace/src/plugins/util/user-provided-git.js +1 -1
  250. package/packages/dd-trace/src/plugins/util/web.js +24 -2
  251. package/packages/dd-trace/src/priority_sampler.js +6 -3
  252. package/packages/dd-trace/src/profiler.js +1 -1
  253. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +1 -1
  254. package/packages/dd-trace/src/profiling/profilers/events.js +2 -2
  255. package/packages/dd-trace/src/profiling/profilers/poisson.js +1 -1
  256. package/packages/dd-trace/src/profiling/profilers/shared.js +2 -2
  257. package/packages/dd-trace/src/proxy.js +1 -0
  258. package/packages/dd-trace/src/ritm.js +10 -20
  259. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +4 -2
  260. package/packages/dd-trace/src/sampling_rule.js +4 -2
  261. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +1 -1
  262. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +1 -1
  263. package/packages/dd-trace/src/startup-log.js +3 -0
  264. package/packages/dd-trace/src/telemetry/logs/index.js +3 -0
  265. package/packages/dd-trace/src/telemetry/logs/log-collector.js +3 -1
  266. package/packages/dd-trace/src/telemetry/telemetry.js +1 -1
  267. package/packages/dd-trace/src/util.js +16 -0
  268. package/vendor/dist/@apm-js-collab/code-transformer/index.js +12 -12
  269. package/vendor/dist/pprof-format/index.js +1 -1
  270. package/vendor/dist/source-map/index.js +1 -1
  271. package/version.js +10 -8
  272. /package/packages/dd-trace/src/appsec/{blocked_templates.js → blocking/templates.js} +0 -0
@@ -7,10 +7,10 @@ const { environmentNamesEqual, setEnvironmentValue } = require('./environment')
7
7
 
8
8
  const RUNNER_OPTIONS = {
9
9
  cucumber: {
10
- flags: new Set(),
10
+ flags: new Set(['--backtrace', '--fail-fast', '--no-strict', '--strict']),
11
11
  values: new Set([
12
12
  '-p', '--config', '--import', '--language', '--loader', '--profile', '--require', '--require-module',
13
- '--world-parameters',
13
+ '--parallel', '--world-parameters',
14
14
  ]),
15
15
  },
16
16
  cypress: {
@@ -18,7 +18,7 @@ const RUNNER_OPTIONS = {
18
18
  values: new Set(['--browser', '--config-file']),
19
19
  },
20
20
  jest: {
21
- flags: new Set(['--detectLeaks']),
21
+ flags: new Set(['--color', '--colors', '--detectLeaks']),
22
22
  values: new Set(['-c', '--config', '--env', '--runner', '--testEnvironment']),
23
23
  },
24
24
  mocha: {
@@ -128,17 +128,6 @@ function getRunnerContract (framework, command, projectRoot, repositoryRoot, pla
128
128
  }
129
129
  }
130
130
  const omittedOptions = getOmittedRunnerOptions(framework, invocation)
131
- if (framework === 'cucumber') {
132
- const language = getOptionValue(invocation, '--language')
133
- if (language && language !== 'en') {
134
- return {
135
- environment: {},
136
- error: `--language ${language} is not supported by the validator-generated English scenarios`,
137
- inputFiles: [],
138
- runnerArgs: [],
139
- }
140
- }
141
- }
142
131
 
143
132
  const environment = getRunnerEnvironment(invocation, platform)
144
133
  const environmentError = getRunnerEnvironmentError(environment, platform)
@@ -167,6 +156,23 @@ function getRunnerContract (framework, command, projectRoot, repositoryRoot, pla
167
156
  }
168
157
  }
169
158
 
159
+ function getRunnerConfigurationContract (framework, runnerArgs, environment, projectRoot, repositoryRoot) {
160
+ const runnerArgsError = getRunnerArgsError(framework, runnerArgs)
161
+ if (runnerArgsError) {
162
+ return {
163
+ environment: {},
164
+ error: `runner arguments ${runnerArgsError}`,
165
+ inputFiles: [],
166
+ runnerArgs: [],
167
+ }
168
+ }
169
+ const inputs = getRunnerInputs(runnerArgs, environment, projectRoot, repositoryRoot)
170
+ if (inputs.error) {
171
+ return { environment: {}, error: inputs.error, inputFiles: [], runnerArgs: [] }
172
+ }
173
+ return { environment, inputFiles: inputs.files, runnerArgs }
174
+ }
175
+
170
176
  /**
171
177
  * Rejects shell-expanded allowlisted environment assignments before command tokenization.
172
178
  *
@@ -239,7 +245,7 @@ function getRunnerSearchRoots (framework, command, projectRoot, repositoryRoot)
239
245
  */
240
246
  function addRunnerSearchRoot (roots, value, projectRoot, repositoryRoot, directory) {
241
247
  if (typeof value !== 'string' || !value) return
242
- const literal = value.split(/[*?{[]/, 1)[0].replace(/[\\/]+$/, '')
248
+ const literal = value.split(/[*?{[]/, 1)[0].replace(/(?<![\\/])[\\/]+$/, '')
243
249
  if (!literal) return
244
250
  const candidate = path.resolve(projectRoot, literal)
245
251
  const searchRoot = directory || !path.extname(candidate) ? candidate : path.dirname(candidate)
@@ -305,17 +311,24 @@ function getUnknownRunnerOption (framework, invocation) {
305
311
 
306
312
  function getOmittedRunnerOptions (framework, invocation) {
307
313
  const omitted = OMITTED_RUNNER_OPTIONS[framework] || new Set()
314
+ const ignored = IGNORED_RUNNER_OPTIONS[framework] || { flags: new Set(), values: new Set() }
308
315
  return [...new Set(invocation.tokens
309
316
  .slice(invocation.runnerIndex + 1)
310
- .filter(token => token.startsWith('-') && omitted.has(token.split('=', 1)[0])))]
317
+ .filter(token => {
318
+ if (!token.startsWith('-')) return false
319
+ const option = token.split('=', 1)[0]
320
+ return omitted.has(option) || ignored.flags.has(option) || ignored.values.has(option)
321
+ })
322
+ .map(token => token.split('=', 1)[0]))]
311
323
  }
312
324
 
313
- function getOptionValue (invocation, expected) {
314
- const tokens = invocation.tokens.slice(invocation.runnerIndex + 1)
315
- for (let index = 0; index < tokens.length; index++) {
316
- if (tokens[index].split('=', 1)[0] !== expected) continue
317
- return tokens[index].includes('=') ? tokens[index].slice(tokens[index].indexOf('=') + 1) : tokens[index + 1]
325
+ function getArgumentOptionValues (args, expected) {
326
+ const values = []
327
+ for (let index = 0; index < args.length; index++) {
328
+ if (args[index].split('=', 1)[0] !== expected) continue
329
+ values.push(args[index].includes('=') ? args[index].slice(args[index].indexOf('=') + 1) : args[index + 1])
318
330
  }
331
+ return values
319
332
  }
320
333
 
321
334
  /**
@@ -361,7 +374,8 @@ function getRunnerArgsError (framework, args) {
361
374
  }
362
375
  const option = token.split('=', 1)[0]
363
376
  if (options.flags.has(option)) {
364
- if (token !== option && !/^--detectLeaks=(?:true|false)$/.test(token)) {
377
+ if (token !== option &&
378
+ !/^--(?:color|colors|detectLeaks)=(?:true|false)$/.test(token)) {
365
379
  return `contain an invalid value for ${option}`
366
380
  }
367
381
  continue
@@ -376,6 +390,13 @@ function getRunnerArgsError (framework, args) {
376
390
  return `contain a missing or unsafe value for ${option}`
377
391
  }
378
392
  }
393
+
394
+ if (framework === 'cucumber') {
395
+ const language = getArgumentOptionValues(args, '--language').find(value => value !== 'en')
396
+ if (language) {
397
+ return `--language ${language} is not supported by the validator-generated English scenarios`
398
+ }
399
+ }
379
400
  }
380
401
 
381
402
  /**
@@ -411,10 +432,12 @@ function getRunnerInputError (args, environment, projectRoot, repositoryRoot, co
411
432
  if (inputs.error) return inputs.error
412
433
 
413
434
  const approved = new Set()
414
- for (const filename of configFiles || []) {
415
- try {
416
- approved.add(fs.realpathSync(filename))
417
- } catch {}
435
+ if (configFiles) {
436
+ for (const filename of configFiles) {
437
+ try {
438
+ approved.add(fs.realpathSync(filename))
439
+ } catch {}
440
+ }
418
441
  }
419
442
  const unbound = inputs.files.find(filename => !approved.has(filename))
420
443
  if (unbound) return `references an input that is not approval-bound: ${unbound}`
@@ -439,26 +462,34 @@ function getFrameworkInvocation (command, framework) {
439
462
  })
440
463
  if (runnerIndex === -1) return
441
464
 
442
- const prefix = tokens.slice(0, runnerIndex)
443
- const firstCommand = prefix.find(token => !/^[A-Za-z_][A-Za-z0-9_]*=/.test(token))
444
- if (firstCommand && !RUNNER_LAUNCHERS.has(path.basename(firstCommand).toLowerCase())) {
445
- return {
446
- error: `runner launch wrapper ${path.basename(firstCommand)} is not allowlisted`,
447
- runnerIndex,
448
- tokens,
449
- }
450
- }
451
- for (const token of prefix) {
452
- if (RUNNER_LAUNCHERS.has(path.basename(token).toLowerCase())) continue
465
+ const prefixError = getRunnerPrefixError(tokens.slice(0, runnerIndex))
466
+ if (prefixError) return { error: prefixError, runnerIndex, tokens }
467
+ return { runnerIndex, tokens }
468
+ }
469
+
470
+ function getRunnerPrefixError (prefix) {
471
+ let coverageLauncher = false
472
+ for (let index = 0; index < prefix.length; index++) {
473
+ const token = prefix[index]
453
474
  if (/^[A-Za-z_][A-Za-z0-9_]*=[^;&|`]*$/.test(token)) continue
454
- return {
455
- error: 'runner launch wrapper contains options or positional arguments whose semantics the validator does not ' +
456
- 'preserve',
457
- runnerIndex,
458
- tokens,
475
+
476
+ const basename = path.basename(token).toLowerCase()
477
+ if (RUNNER_LAUNCHERS.has(basename)) {
478
+ coverageLauncher = basename === 'c8' || basename === 'nyc'
479
+ continue
459
480
  }
481
+ if (coverageLauncher && /^(?:-r|--reporter)=\S+$/.test(token)) continue
482
+ if (coverageLauncher && /^(?:-r|--reporter)$/.test(token) &&
483
+ prefix[index + 1] && !prefix[index + 1].startsWith('-')) {
484
+ index++
485
+ continue
486
+ }
487
+ if (!prefix.some(candidate => RUNNER_LAUNCHERS.has(path.basename(candidate).toLowerCase()))) {
488
+ return `runner launch wrapper ${path.basename(token)} is not allowlisted`
489
+ }
490
+ return 'runner launch wrapper contains options or positional arguments whose semantics the validator does not ' +
491
+ 'preserve'
460
492
  }
461
- return { runnerIndex, tokens }
462
493
  }
463
494
 
464
495
  /**
@@ -612,6 +643,10 @@ function resolveInputFile (input, projectRoot) {
612
643
  if (!input.module || path.isAbsolute(input.value) || input.value.startsWith('.')) {
613
644
  return path.resolve(projectRoot, input.value)
614
645
  }
646
+ const projectRelative = path.resolve(projectRoot, input.value)
647
+ try {
648
+ return require.resolve(projectRelative)
649
+ } catch {}
615
650
  try {
616
651
  return require.resolve(input.value, { paths: [projectRoot] })
617
652
  } catch {}
@@ -657,6 +692,7 @@ function isPathInside (root, filename) {
657
692
  module.exports = {
658
693
  getProjectNodeRunner,
659
694
  getRunnerArgsError,
695
+ getRunnerConfigurationContract,
660
696
  getRunnerContract,
661
697
  getRunnerEnvironmentError,
662
698
  getRunnerInputError,
@@ -129,10 +129,10 @@ function openAndWrite (root, filename, data, label, creationFlag) {
129
129
  *
130
130
  * @param {string} directory directory path
131
131
  * @param {string} label customer-facing path label
132
- * @returns {{dev: number, ino: number}} directory identity
132
+ * @returns {{dev: bigint, ino: bigint}} directory identity
133
133
  */
134
134
  function getDirectoryIdentity (directory, label) {
135
- const stat = fs.lstatSync(directory)
135
+ const stat = fs.lstatSync(directory, { bigint: true })
136
136
  if (!stat.isDirectory() || stat.isSymbolicLink()) {
137
137
  throw new Error(`Refusing ${label} because its parent is not a regular directory: ${directory}`)
138
138
  }
@@ -143,7 +143,7 @@ function getDirectoryIdentity (directory, label) {
143
143
  * Refuses replacement of a parent directory between safe creation and publication.
144
144
  *
145
145
  * @param {string} directory directory path
146
- * @param {{dev: number, ino: number}} expected expected identity
146
+ * @param {{dev: bigint, ino: bigint}} expected expected identity
147
147
  * @param {string} label customer-facing path label
148
148
  */
149
149
  function assertDirectoryIdentity (directory, expected, label) {
@@ -158,7 +158,7 @@ function assertDirectoryIdentity (directory, expected, label) {
158
158
  *
159
159
  * @param {string} filename temporary filename
160
160
  * @param {string} parent expected parent directory
161
- * @param {{dev: number, ino: number}} parentIdentity expected parent identity
161
+ * @param {{dev: bigint, ino: bigint}} parentIdentity expected parent identity
162
162
  */
163
163
  function removeTemporaryFile (filename, parent, parentIdentity) {
164
164
  try {
@@ -58,7 +58,7 @@ async function runBasicReporting ({ framework, out, options }) {
58
58
  selector,
59
59
  settingsLoadedFromCache: run.offline.inputs.settings?.status === 'loaded',
60
60
  }
61
- evidence.foundationalReportingEstablished = evidence.foundationalReportingEstablished && selector.verified
61
+ evidence.foundationalReportingEstablished &&= selector.verified
62
62
 
63
63
  if (run.result.timedOut) {
64
64
  return inconclusive(
@@ -111,7 +111,7 @@ async function runBasicReporting ({ framework, out, options }) {
111
111
  }
112
112
 
113
113
  if (!run.offline.initialized || !evidence.settingsLoadedFromCache) {
114
- return failWithDebugRerun({
114
+ return failBasicWithDebugRerun({
115
115
  command,
116
116
  diagnosis: run.offline.initialized
117
117
  ? 'The clean test passed, but Test Optimization did not load the validator-owned offline settings.'
@@ -131,7 +131,7 @@ async function runBasicReporting ({ framework, out, options }) {
131
131
  }
132
132
 
133
133
  if (run.result.exitCode !== 0) {
134
- evidence.commandFailure = classifyFailure(framework, run.result)
134
+ evidence.commandFailure = classifyFailure(framework, run.result, run.events)
135
135
  const confirmation = await runCleanConfirmation({ command, framework, options, out })
136
136
  evidence.cleanConfirmation = confirmation.evidence
137
137
  if (!confirmation.evidence.matchesPreflight) {
@@ -144,7 +144,7 @@ async function runBasicReporting ({ framework, out, options }) {
144
144
  confirmation.artifacts
145
145
  )
146
146
  }
147
- const result = await failWithDebugRerun({
147
+ const result = await failBasicWithDebugRerun({
148
148
  command,
149
149
  diagnosis: complete
150
150
  ? `The test passed twice without Datadog but exited ${run.result.exitCode} when initialized, after ` +
@@ -169,7 +169,7 @@ async function runBasicReporting ({ framework, out, options }) {
169
169
  }
170
170
 
171
171
  const missing = getMissingLevels(evidence)
172
- return failWithDebugRerun({
172
+ return failBasicWithDebugRerun({
173
173
  command,
174
174
  diagnosis: 'The direct test passed cleanly and while initialized, but no complete Test Optimization event ' +
175
175
  `hierarchy was captured. Missing levels: ${missing.join(', ')}. This is a possible dd-trace adapter bug.`,
@@ -194,6 +194,50 @@ async function runBasicReporting ({ framework, out, options }) {
194
194
  }
195
195
  }
196
196
 
197
+ async function failBasicWithDebugRerun (input) {
198
+ const result = await failWithDebugRerun(input)
199
+ const debug = result.evidence?.debugRerun
200
+ if (!isSuccessfulDebugRerun(debug, result.evidence?.selector)) return result
201
+
202
+ const initialDiagnosis = result.diagnosis
203
+ delete result.evidence.possibleLibraryBug
204
+ result.status = 'error'
205
+ result.diagnosis = 'The first initialized run failed, but the unchanged DD_TRACE_DEBUG=1 rerun exited cleanly ' +
206
+ 'and emitted the complete event hierarchy. Basic Reporting is intermittent, so no library-bug conclusion ' +
207
+ 'was reached.'
208
+ result.evidence.initialInstrumentedDiagnosis = initialDiagnosis
209
+ result.evidence.intermittentInstrumentedResult = true
210
+ result.evidence.recommendation = 'Repeat the approved initialized command without changing its test selection. ' +
211
+ 'If the failure recurs, send both initialized runs and the clean confirmations to engineering.'
212
+ result.evidence.validationIncomplete = true
213
+ return result
214
+ }
215
+
216
+ function isSuccessfulDebugRerun (debug, selector) {
217
+ return isDebugSelectorVerified(debug, selector) &&
218
+ debug?.ran === true &&
219
+ debug.commandExitCode === 0 &&
220
+ debug.commandTimedOut === false &&
221
+ debug.offlineExporterInitialized === true &&
222
+ debug.settingsLoadedFromCache === true &&
223
+ debug.testSessionEvents > 0 &&
224
+ debug.testModuleEvents > 0 &&
225
+ debug.testSuiteEvents > 0 &&
226
+ debug.testEvents > 0
227
+ }
228
+
229
+ // A wrapper rerun is comparable only when events identify the approved representative and no other file.
230
+ function isDebugSelectorVerified (debug, selector) {
231
+ if (selector?.verified !== true) return false
232
+ if (selector.mode === 'bounded_direct_runner') return true
233
+ if (selector.mode !== 'instrumented_event_identity' ||
234
+ typeof selector.expectedTestFile !== 'string' ||
235
+ debug?.testEventsWithoutSourceFile !== 0 ||
236
+ debug?.testSourceFileCount !== 1 ||
237
+ debug?.testSourceFiles?.length !== 1) return false
238
+ return sourceFilesMatch(debug.testSourceFiles[0], selector.expectedTestFile)
239
+ }
240
+
197
241
  /**
198
242
  * Repeats the direct clean command after an initialized-only failure.
199
243
  *
@@ -234,10 +278,17 @@ async function runCleanConfirmation ({ command, framework, options, out }) {
234
278
  */
235
279
  function summarizePreflight (preflight = {}) {
236
280
  return {
281
+ attempts: (preflight.attempts || []).map(attempt => ({
282
+ exitCode: attempt.exitCode,
283
+ observedTestCount: attempt.observedTestCount,
284
+ testFile: attempt.testFile,
285
+ timedOut: attempt.timedOut === true,
286
+ })),
237
287
  durationMs: preflight.durationMs,
238
288
  exitCode: preflight.exitCode,
239
289
  observedTestCount: preflight.observedTestCount,
240
290
  ran: preflight.ran === true,
291
+ selectedTestFile: preflight.selectedTestFile,
241
292
  selectorVerification: preflight.selectorVerification,
242
293
  timedOut: preflight.timedOut === true,
243
294
  }
@@ -312,12 +363,15 @@ function normalizeSourceFile (filename) {
312
363
  *
313
364
  * @param {object} framework framework entry
314
365
  * @param {object} result command result
366
+ * @param {object[]} [events] normalized captured events
315
367
  * @returns {object|undefined} blocker
316
368
  */
317
- function classifyFailure (framework, result) {
369
+ function classifyFailure (framework, result, events = []) {
318
370
  return getCommandBlocker(result, {
319
371
  browserRequired: framework.browserRequired,
320
372
  framework: framework.framework,
373
+ packageJson: framework.project?.packageJson,
374
+ testsRan: eventsOfType(events, 'test').length > 0,
321
375
  })
322
376
  }
323
377