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
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
- const fs = require('fs')
4
- const path = require('path')
3
+ const fs = require('node:fs')
4
+ const path = require('node:path')
5
5
 
6
6
  const {
7
7
  MAX_GENERATED_FILES,
@@ -91,14 +91,16 @@ function cleanupGeneratedFiles (manifest, { keep = false } = {}) {
91
91
  filesRemoved: 0,
92
92
  filesRetained: 0,
93
93
  }
94
- for (const framework of manifest.frameworks || []) {
95
- const strategy = framework.generatedTestStrategy
96
- addCleanupOutcome(
97
- outcome,
98
- cleanupPaths(getSafeCleanupPaths(framework, strategy, { includeGeneratedFiles: true })),
99
- 'files'
100
- )
101
- addCleanupOutcome(outcome, cleanupCreatedDirectories(framework.project.root), 'directories')
94
+ if (manifest.frameworks) {
95
+ for (const framework of manifest.frameworks) {
96
+ const strategy = framework.generatedTestStrategy
97
+ addCleanupOutcome(
98
+ outcome,
99
+ cleanupPaths(getSafeCleanupPaths(framework, strategy, { includeGeneratedFiles: true })),
100
+ 'files'
101
+ )
102
+ addCleanupOutcome(outcome, cleanupCreatedDirectories(framework.project.root), 'directories')
103
+ }
102
104
  }
103
105
  outcome.status = outcome.filesRetained > 0 || outcome.directoriesRetained > 0
104
106
  ? 'incomplete'
@@ -132,7 +134,7 @@ function getMissingDirectories (root, directory) {
132
134
  function cleanupCreatedDirectories (root) {
133
135
  const outcome = { removed: 0, retained: 0 }
134
136
  const resolvedRoot = path.resolve(root)
135
- const directories = [...createdGeneratedDirectories.entries()]
137
+ const directories = [...createdGeneratedDirectories]
136
138
  .filter(([directory]) => isPathInside(resolvedRoot, directory))
137
139
  .sort(([left], [right]) => right.length - left.length)
138
140
 
@@ -168,13 +170,15 @@ function initializeRuntimeCleanupFiles (framework, strategy) {
168
170
  if (initializedCleanupStrategies.has(strategy)) return
169
171
 
170
172
  const generatedFiles = new Set((strategy.files || []).map(file => validateGeneratedFilePath(framework, file.path)))
171
- for (const cleanupPath of strategy.cleanupPaths || []) {
172
- const filename = validateCleanupPath(framework, cleanupPath)
173
- if (generatedFiles.has(filename) || isDirectory(filename) || !isNamespacedRuntimeFile(filename)) continue
174
- if (fs.existsSync(filename)) {
175
- throw new Error(`Refusing to delete pre-existing generated validation runtime file: ${filename}`)
173
+ if (strategy.cleanupPaths) {
174
+ for (const cleanupPath of strategy.cleanupPaths) {
175
+ const filename = validateCleanupPath(framework, cleanupPath)
176
+ if (generatedFiles.has(filename) || isDirectory(filename) || !isNamespacedRuntimeFile(filename)) continue
177
+ if (fs.existsSync(filename)) {
178
+ throw new Error(`Refusing to delete pre-existing generated validation runtime file: ${filename}`)
179
+ }
180
+ authorizedRuntimeCleanupFiles.set(filename, authorizePathForCleanup(framework.project.root, filename))
176
181
  }
177
- authorizedRuntimeCleanupFiles.set(filename, authorizePathForCleanup(framework.project.root, filename))
178
182
  }
179
183
  initializedCleanupStrategies.add(strategy)
180
184
  }
@@ -183,20 +187,24 @@ function getSafeCleanupPaths (framework, strategy, { includeGeneratedFiles }) {
183
187
  if (!strategy) return []
184
188
 
185
189
  const generatedFiles = new Set()
186
- for (const file of strategy.files || []) {
187
- generatedFiles.add(validateGeneratedFilePath(framework, file.path))
190
+ if (strategy.files) {
191
+ for (const file of strategy.files) {
192
+ generatedFiles.add(validateGeneratedFilePath(framework, file.path))
193
+ }
188
194
  }
189
195
 
190
196
  const cleanupPaths = []
191
- for (const cleanupPath of strategy.cleanupPaths || []) {
192
- const filename = validateCleanupPath(framework, cleanupPath)
193
- if (generatedFiles.has(filename)) {
194
- if (includeGeneratedFiles && writtenGeneratedFiles.has(filename)) cleanupPaths.push(filename)
195
- continue
196
- }
197
+ if (strategy.cleanupPaths) {
198
+ for (const cleanupPath of strategy.cleanupPaths) {
199
+ const filename = validateCleanupPath(framework, cleanupPath)
200
+ if (generatedFiles.has(filename)) {
201
+ if (includeGeneratedFiles && writtenGeneratedFiles.has(filename)) cleanupPaths.push(filename)
202
+ continue
203
+ }
197
204
 
198
- if (authorizedRuntimeCleanupFiles.has(filename)) {
199
- cleanupPaths.push(filename)
205
+ if (authorizedRuntimeCleanupFiles.has(filename)) {
206
+ cleanupPaths.push(filename)
207
+ }
200
208
  }
201
209
  }
202
210
 
@@ -251,7 +259,7 @@ function forgetWrittenGeneratedFiles (filenames) {
251
259
  function authorizePathForCleanup (root, filename) {
252
260
  const lexicalRoot = path.resolve(root)
253
261
  const physicalRoot = fs.realpathSync(lexicalRoot)
254
- const rootStat = fs.statSync(physicalRoot)
262
+ const rootStat = fs.statSync(physicalRoot, { bigint: true })
255
263
  const authorization = {
256
264
  lexicalRoot,
257
265
  physicalRoot,
@@ -264,10 +272,12 @@ function authorizePathForCleanup (root, filename) {
264
272
  }
265
273
 
266
274
  function pinRuntimeCleanupParents (strategy) {
267
- for (const cleanupPath of strategy.cleanupPaths || []) {
268
- const authorization = authorizedRuntimeCleanupFiles.get(path.resolve(cleanupPath))
269
- if (authorization && authorization.physicalParent === undefined) {
270
- pinCleanupParent(authorization, cleanupPath)
275
+ if (strategy.cleanupPaths) {
276
+ for (const cleanupPath of strategy.cleanupPaths) {
277
+ const authorization = authorizedRuntimeCleanupFiles.get(path.resolve(cleanupPath))
278
+ if (authorization && authorization.physicalParent === undefined) {
279
+ pinCleanupParent(authorization, cleanupPath)
280
+ }
271
281
  }
272
282
  }
273
283
  }
@@ -276,7 +286,7 @@ function pinCleanupParent (authorization, filename) {
276
286
  try {
277
287
  const physicalParent = fs.realpathSync(path.dirname(filename))
278
288
  if (!isPathInside(authorization.physicalRoot, physicalParent)) return
279
- const parentStat = fs.statSync(physicalParent)
289
+ const parentStat = fs.statSync(physicalParent, { bigint: true })
280
290
  authorization.physicalParent = physicalParent
281
291
  authorization.parentDevice = parentStat.dev
282
292
  authorization.parentInode = parentStat.ino
@@ -285,7 +295,7 @@ function pinCleanupParent (authorization, filename) {
285
295
 
286
296
  function pinCleanupTarget (authorization, filename) {
287
297
  try {
288
- const targetStat = fs.lstatSync(filename)
298
+ const targetStat = fs.lstatSync(filename, { bigint: true })
289
299
  authorization.targetDevice = targetStat.dev
290
300
  authorization.targetInode = targetStat.ino
291
301
  } catch {}
@@ -294,7 +304,7 @@ function pinCleanupTarget (authorization, filename) {
294
304
  function isCleanupAuthorizationValid (filename, authorization) {
295
305
  try {
296
306
  const currentPhysicalRoot = fs.realpathSync(authorization.lexicalRoot)
297
- const rootStat = fs.statSync(currentPhysicalRoot)
307
+ const rootStat = fs.statSync(currentPhysicalRoot, { bigint: true })
298
308
  if (currentPhysicalRoot !== authorization.physicalRoot ||
299
309
  rootStat.dev !== authorization.rootDevice || rootStat.ino !== authorization.rootInode) {
300
310
  return false
@@ -302,14 +312,14 @@ function isCleanupAuthorizationValid (filename, authorization) {
302
312
 
303
313
  if (authorization.physicalParent === undefined) return false
304
314
  const physicalParent = fs.realpathSync(path.dirname(filename))
305
- const parentStat = fs.statSync(physicalParent)
315
+ const parentStat = fs.statSync(physicalParent, { bigint: true })
306
316
  if (physicalParent !== authorization.physicalParent ||
307
317
  parentStat.dev !== authorization.parentDevice || parentStat.ino !== authorization.parentInode) {
308
318
  return false
309
319
  }
310
320
 
311
321
  if (authorization.targetDevice !== undefined) {
312
- const targetStat = fs.lstatSync(filename)
322
+ const targetStat = fs.lstatSync(filename, { bigint: true })
313
323
  if (targetStat.dev !== authorization.targetDevice || targetStat.ino !== authorization.targetInode) {
314
324
  return false
315
325
  }
@@ -79,7 +79,7 @@ function getGeneratedTestContent ({ framework, moduleSystem, scenarioId, stateFi
79
79
  const requireCall = 'require'
80
80
  imports.push(moduleSystem === 'esm'
81
81
  ? "import { describe, expect, test } from '@jest/globals'"
82
- : `const { describe, expect, test } = ${requireCall}('@jest/globals')`)
82
+ : `const jestGlobals = ${requireCall}('@jest/globals')`)
83
83
  }
84
84
  if (framework === 'vitest' && moduleSystem === 'esm') {
85
85
  imports.push("import { describe, expect, it } from 'vitest'")
@@ -97,8 +97,12 @@ function getGeneratedTestContent ({ framework, moduleSystem, scenarioId, stateFi
97
97
  }
98
98
  }
99
99
 
100
- const assertion = framework === 'mocha' ? 'assert.equal(1, 1)' : 'expect(true).toBe(true)'
101
- const testFunction = framework === 'jest' ? 'test' : 'it'
100
+ const commonJsJest = framework === 'jest' && moduleSystem === 'commonjs'
101
+ const assertion = framework === 'mocha'
102
+ ? 'assert.equal(1, 1)'
103
+ : `${commonJsJest ? 'jestGlobals.' : ''}expect(true).toBe(true)`
104
+ const testFunction = framework === 'jest' ? `${commonJsJest ? 'jestGlobals.' : ''}test` : 'it'
105
+ const describeFunction = commonJsJest ? 'jestGlobals.describe' : 'describe'
102
106
  const body = scenarioId === 'atr-fail-once'
103
107
  ? getAtrBody({ moduleSystem, assertion, stateFile })
104
108
  : ` ${assertion}`
@@ -106,7 +110,7 @@ function getGeneratedTestContent ({ framework, moduleSystem, scenarioId, stateFi
106
110
  return [
107
111
  ...imports,
108
112
  imports.length > 0 ? '' : undefined,
109
- "describe('dd-test-optimization-validation', () => {",
113
+ `${describeFunction}('dd-test-optimization-validation', () => {`,
110
114
  ` ${testFunction}('${scenario.testName}', () => {`,
111
115
  body,
112
116
  ' })',
@@ -192,9 +196,7 @@ function getGeneratedTestContractError (framework) {
192
196
  if (!cleanupPaths.has(path.normalize(stepsFile))) {
193
197
  return 'must include the isolated Cucumber step definitions in cleanupPaths.'
194
198
  }
195
- }
196
-
197
- if (framework.framework === 'playwright') {
199
+ } else if (framework.framework === 'playwright') {
198
200
  const configPath = playwrightAdapter.getGeneratedConfigPath(strategy.testDirectory)
199
201
  const config = files.find(file => path.normalize(file.path) === path.normalize(configPath))
200
202
  if (config?.contentLines?.join('\n') !== playwrightAdapter.getGeneratedConfigContent()) {
@@ -3,6 +3,7 @@
3
3
  const fs = require('node:fs')
4
4
  const path = require('node:path')
5
5
 
6
+ const { BLOCKER_CATEGORIES, getBlockerDomain } = require('./blocker-category')
6
7
  const { getCommandBlocker } = require('./command-blocker')
7
8
  const { runCommand, serializeDisplayCommand } = require('./command-runner')
8
9
  const {
@@ -12,6 +13,7 @@ const {
12
13
  const { getGeneratedCommand } = require('./runner-command')
13
14
  const { frameworkOutDir } = require('./scenarios/helpers')
14
15
  const { getObservedTestCount } = require('./test-output')
16
+ const { getValidationBlockerEvidence } = require('./validation-blocker')
15
17
 
16
18
  const GENERATED_SCENARIO_BY_FEATURE = {
17
19
  efd: 'basic-pass',
@@ -103,15 +105,16 @@ async function verifyGeneratedTestStrategy ({ framework, out, options }) {
103
105
  return { ok: true }
104
106
  } catch (error) {
105
107
  cleanupGeneratedRuntimeFiles(framework)
108
+ const blockerEvidence = getValidationBlockerEvidence(error)
106
109
  return {
107
110
  ok: false,
108
111
  failure: {
109
112
  frameworkId: framework.id,
110
113
  scenario: 'generated-test-verification',
111
- status: 'error',
114
+ status: blockerEvidence ? 'blocked' : 'error',
112
115
  diagnosis: 'The validator could not run the temporary validation test as expected. No advanced-feature ' +
113
116
  `conclusion was reached: ${error.message || error}`,
114
- evidence,
117
+ evidence: { ...evidence, ...blockerEvidence },
115
118
  artifacts,
116
119
  },
117
120
  }
@@ -153,14 +156,12 @@ function getVerificationFailure (framework, evidence, artifacts, scenario, resul
153
156
  const commandFailure = getCommandBlocker(result, {
154
157
  browserRequired: framework.browserRequired,
155
158
  framework: framework.framework,
159
+ packageJson: framework.project.packageJson,
156
160
  testsRan: Number.isInteger(observedTestCount) && observedTestCount > 0,
157
161
  })
158
162
  if (commandFailure) {
159
- const domain = commandFailure.blockedByExecutionEnvironment
160
- ? 'execution_environment'
161
- : commandFailure.localRuntimeBlocked
162
- ? 'local_runtime'
163
- : 'project_setup'
163
+ const blockerCategory = commandFailure.blockerCategory
164
+ const domain = getBlockerDomain(blockerCategory)
164
165
  return {
165
166
  ok: false,
166
167
  failure: {
@@ -168,7 +169,7 @@ function getVerificationFailure (framework, evidence, artifacts, scenario, resul
168
169
  scenario: 'generated-test-verification',
169
170
  status: 'blocked',
170
171
  diagnosis: `${commandFailure.summary} Advanced-feature validation could not start reliably.`,
171
- evidence: { ...evidence, commandFailure, domain, validationIncomplete: true },
172
+ evidence: { ...evidence, blockerCategory, commandFailure, domain, validationIncomplete: true },
172
173
  artifacts,
173
174
  },
174
175
  }
@@ -189,7 +190,11 @@ function getVerificationFailure (framework, evidence, artifacts, scenario, resul
189
190
  scenario: 'generated-test-verification',
190
191
  status: 'error',
191
192
  diagnosis: `Temporary validation test "${scenario.id}" ${reason}. No advanced-feature conclusion was reached.`,
192
- evidence,
193
+ evidence: {
194
+ ...evidence,
195
+ blockerCategory: BLOCKER_CATEGORIES.VALIDATOR_LIMITATION,
196
+ validationIncomplete: true,
197
+ },
193
198
  artifacts,
194
199
  },
195
200
  }
@@ -204,14 +209,16 @@ function getVerificationFailure (framework, evidence, artifacts, scenario, resul
204
209
  function getGeneratedRuntimeFileStatus (strategy) {
205
210
  const generatedFiles = new Set((strategy.files || []).map(file => path.resolve(file.path)))
206
211
  let expectsRuntimeFile = false
207
- for (const cleanupPath of strategy.cleanupPaths || []) {
208
- const filename = path.resolve(cleanupPath)
209
- if (generatedFiles.has(filename)) continue
210
- expectsRuntimeFile = true
211
- try {
212
- const stat = fs.lstatSync(filename)
213
- if (!stat.isSymbolicLink() && stat.isFile()) return true
214
- } catch {}
212
+ if (strategy.cleanupPaths) {
213
+ for (const cleanupPath of strategy.cleanupPaths) {
214
+ const filename = path.resolve(cleanupPath)
215
+ if (generatedFiles.has(filename)) continue
216
+ expectsRuntimeFile = true
217
+ try {
218
+ const stat = fs.lstatSync(filename)
219
+ if (!stat.isSymbolicLink() && stat.isFile()) return true
220
+ } catch {}
221
+ }
215
222
  }
216
223
  return expectsRuntimeFile ? false : undefined
217
224
  }
@@ -0,0 +1,52 @@
1
+ 'use strict'
2
+
3
+ // Dynamic extglobs, negation, character classes, and malformed braces fail closed.
4
+ function matchesLiteralGlob (filename, pattern) {
5
+ const source = String(pattern || '').replaceAll('\\', '/').replace(/^\.\//, '')
6
+ if (!source || /[!()[\]]/.test(source)) return false
7
+
8
+ let expression = '^'
9
+ for (let index = 0; index < source.length; index++) {
10
+ const character = source[index]
11
+ if (character === '*') {
12
+ if (source[index + 1] === '*') {
13
+ index++
14
+ if (source[index + 1] === '/') {
15
+ index++
16
+ expression += '(?:.*/)?'
17
+ } else {
18
+ expression += '.*'
19
+ }
20
+ } else {
21
+ expression += '[^/]*'
22
+ }
23
+ continue
24
+ }
25
+ if (character === '?') {
26
+ expression += '[^/]'
27
+ continue
28
+ }
29
+ if (character === '{') {
30
+ const end = source.indexOf('}', index + 1)
31
+ if (end === -1) return false
32
+ const alternatives = source.slice(index + 1, end).split(',')
33
+ if (alternatives.length < 2 || alternatives.some(value => !/^[A-Za-z0-9._-]+$/.test(value))) return false
34
+ expression += `(?:${alternatives.map(escapeRegExp).join('|')})`
35
+ // eslint-disable-next-line sonarjs/updated-loop-counter
36
+ index = end
37
+ continue
38
+ }
39
+ expression += escapeRegExp(character)
40
+ }
41
+ try {
42
+ return new RegExp(`${expression}$`).test(String(filename).replaceAll('\\', '/').replaceAll(/^\.\//g, ''))
43
+ } catch {
44
+ return false
45
+ }
46
+ }
47
+
48
+ function escapeRegExp (value) {
49
+ return value.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`)
50
+ }
51
+
52
+ module.exports = { matchesLiteralGlob }
@@ -81,6 +81,9 @@ function getManifestPaths (manifest) {
81
81
  [`${prefix}.ciWiring.configFile`, framework.ciWiring?.configFile],
82
82
  [`${prefix}.ciWiring.workingDirectory`, framework.ciWiring?.workingDirectory]
83
83
  )
84
+ for (const [index, fallback] of (framework.validation?.fallbackTests || []).entries()) {
85
+ paths.push([`${prefix}.validation.fallbackTests[${index}].testFile`, fallback.testFile])
86
+ }
84
87
  for (const [index, configFile] of (framework.project?.configFiles || []).entries()) {
85
88
  paths.push([`${prefix}.project.configFiles[${index}]`, configFile])
86
89
  }