dd-trace 6.4.0 → 6.6.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 (185) hide show
  1. package/README.electron.md +7 -0
  2. package/README.md +17 -0
  3. package/ci/diagnose.js +2100 -0
  4. package/ci/init.js +23 -2
  5. package/ci/runbook.md +198 -0
  6. package/ci/test-optimization-validation/approval-artifacts.js +143 -0
  7. package/ci/test-optimization-validation/approval.js +299 -0
  8. package/ci/test-optimization-validation/artifact-id.js +20 -0
  9. package/ci/test-optimization-validation/bounded-json.js +121 -0
  10. package/ci/test-optimization-validation/ci-command-candidate.js +83 -0
  11. package/ci/test-optimization-validation/ci-discovery.js +181 -0
  12. package/ci/test-optimization-validation/ci-remediation.js +210 -0
  13. package/ci/test-optimization-validation/cli.js +903 -0
  14. package/ci/test-optimization-validation/command-blocker.js +81 -0
  15. package/ci/test-optimization-validation/command-output-policy.js +206 -0
  16. package/ci/test-optimization-validation/command-runner.js +707 -0
  17. package/ci/test-optimization-validation/command-suitability.js +471 -0
  18. package/ci/test-optimization-validation/executable-approval.js +48 -0
  19. package/ci/test-optimization-validation/executable.js +480 -0
  20. package/ci/test-optimization-validation/generated-file-policy.js +63 -0
  21. package/ci/test-optimization-validation/generated-files.js +351 -0
  22. package/ci/test-optimization-validation/generated-verifier.js +196 -0
  23. package/ci/test-optimization-validation/init-probe-preload.js +163 -0
  24. package/ci/test-optimization-validation/init-probe.js +246 -0
  25. package/ci/test-optimization-validation/late-initialization.js +63 -0
  26. package/ci/test-optimization-validation/local-command.js +163 -0
  27. package/ci/test-optimization-validation/manifest-loader.js +133 -0
  28. package/ci/test-optimization-validation/manifest-scaffold.js +738 -0
  29. package/ci/test-optimization-validation/manifest-schema.js +862 -0
  30. package/ci/test-optimization-validation/offline-fixtures.js +327 -0
  31. package/ci/test-optimization-validation/offline-output.js +406 -0
  32. package/ci/test-optimization-validation/payload-normalizer.js +72 -0
  33. package/ci/test-optimization-validation/plan-writer.js +1120 -0
  34. package/ci/test-optimization-validation/preflight-runner.js +114 -0
  35. package/ci/test-optimization-validation/redaction.js +331 -0
  36. package/ci/test-optimization-validation/report-writer.js +1508 -0
  37. package/ci/test-optimization-validation/safe-files.js +203 -0
  38. package/ci/test-optimization-validation/scenarios/auto-test-retries.js +159 -0
  39. package/ci/test-optimization-validation/scenarios/basic-reporting.js +657 -0
  40. package/ci/test-optimization-validation/scenarios/ci-wiring.js +780 -0
  41. package/ci/test-optimization-validation/scenarios/early-flake-detection.js +141 -0
  42. package/ci/test-optimization-validation/scenarios/helpers.js +539 -0
  43. package/ci/test-optimization-validation/scenarios/test-management.js +218 -0
  44. package/ci/test-optimization-validation/setup-runner.js +97 -0
  45. package/ci/test-optimization-validation/static-diagnosis.js +159 -0
  46. package/ci/test-optimization-validation/test-output.js +129 -0
  47. package/ci/test-optimization-validation-manifest.schema.json +1 -0
  48. package/ci/validate-test-optimization.js +3 -0
  49. package/ext/exporters.js +1 -0
  50. package/index.d.ts +121 -8
  51. package/init.js +18 -0
  52. package/initialize.mjs +1 -1
  53. package/loader-hook.mjs +28 -18
  54. package/openfeature.d.ts +1 -0
  55. package/openfeature.js +4 -0
  56. package/package.json +11 -8
  57. package/packages/datadog-instrumentations/src/ai.js +8 -2
  58. package/packages/datadog-instrumentations/src/child_process.js +1 -1
  59. package/packages/datadog-instrumentations/src/claude-agent-sdk.js +1 -1
  60. package/packages/datadog-instrumentations/src/cucumber-worker-threads.js +9 -3
  61. package/packages/datadog-instrumentations/src/cucumber.js +31 -19
  62. package/packages/datadog-instrumentations/src/cypress-config.js +398 -52
  63. package/packages/datadog-instrumentations/src/fastify.js +7 -11
  64. package/packages/datadog-instrumentations/src/helpers/channel.js +74 -0
  65. package/packages/datadog-instrumentations/src/helpers/hook.js +27 -6
  66. package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +8 -8
  67. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +28 -2
  68. package/packages/datadog-instrumentations/src/jest.js +44 -17
  69. package/packages/datadog-instrumentations/src/mariadb.js +1 -1
  70. package/packages/datadog-instrumentations/src/mocha/main.js +79 -32
  71. package/packages/datadog-instrumentations/src/mocha/utils.js +0 -4
  72. package/packages/datadog-instrumentations/src/mongodb.js +3 -3
  73. package/packages/datadog-instrumentations/src/mongoose.js +3 -3
  74. package/packages/datadog-instrumentations/src/mquery.js +2 -2
  75. package/packages/datadog-instrumentations/src/mysql.js +1 -1
  76. package/packages/datadog-instrumentations/src/next.js +24 -0
  77. package/packages/datadog-instrumentations/src/nyc.js +0 -2
  78. package/packages/datadog-instrumentations/src/oracledb.js +2 -2
  79. package/packages/datadog-instrumentations/src/pg.js +2 -2
  80. package/packages/datadog-instrumentations/src/playwright.js +162 -46
  81. package/packages/datadog-instrumentations/src/process.js +3 -0
  82. package/packages/datadog-instrumentations/src/router.js +18 -8
  83. package/packages/datadog-instrumentations/src/selenium.js +52 -26
  84. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +3 -3
  85. package/packages/datadog-instrumentations/src/vitest-main.js +14 -28
  86. package/packages/datadog-instrumentations/src/vitest-util.js +0 -7
  87. package/packages/datadog-instrumentations/src/vitest-worker.js +5 -8
  88. package/packages/datadog-instrumentations/src/ws.js +2 -1
  89. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/context.js +2 -2
  90. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +2 -1
  91. package/packages/datadog-plugin-aws-sdk/src/util.js +2 -2
  92. package/packages/datadog-plugin-cucumber/src/index.js +3 -2
  93. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +19 -57
  94. package/packages/datadog-plugin-cypress/src/index.js +6 -1
  95. package/packages/datadog-plugin-cypress/src/support.js +92 -26
  96. package/packages/datadog-plugin-electron/src/ipc.js +1 -1
  97. package/packages/datadog-plugin-graphql/src/execute.js +87 -45
  98. package/packages/datadog-plugin-graphql/src/validate.js +2 -2
  99. package/packages/datadog-plugin-http2/src/client.js +1 -1
  100. package/packages/datadog-plugin-langchain/src/handlers/embedding.js +2 -1
  101. package/packages/datadog-plugin-langchain/src/handlers/language_models.js +2 -1
  102. package/packages/datadog-plugin-langchain/src/tokens.js +2 -2
  103. package/packages/datadog-plugin-mocha/src/index.js +2 -1
  104. package/packages/datadog-plugin-playwright/src/index.js +181 -65
  105. package/packages/datadog-plugin-router/src/index.js +11 -2
  106. package/packages/datadog-plugin-selenium/src/index.js +5 -36
  107. package/packages/datadog-plugin-vitest/src/index.js +5 -5
  108. package/packages/datadog-plugin-ws/src/close.js +2 -1
  109. package/packages/datadog-plugin-ws/src/producer.js +2 -1
  110. package/packages/datadog-plugin-ws/src/receiver.js +2 -1
  111. package/packages/datadog-plugin-ws/src/server.js +2 -1
  112. package/packages/dd-trace/src/aiguard/sdk.js +1 -1
  113. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +2 -2
  114. package/packages/dd-trace/src/appsec/iast/telemetry/span-tags.js +3 -1
  115. package/packages/dd-trace/src/azure_metadata.js +2 -1
  116. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +93 -28
  117. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +1 -1
  118. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/index.js +160 -0
  119. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/msgpack-to-json.js +288 -0
  120. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/payload-projection.js +84 -0
  121. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/sink.js +369 -0
  122. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/writer.js +60 -0
  123. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +41 -3
  124. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +7 -2
  125. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +1 -1
  126. package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +6 -2
  127. package/packages/dd-trace/src/ci-visibility/rum.js +7 -0
  128. package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +4 -0
  129. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +1 -1
  130. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +137 -9
  131. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +168 -8
  132. package/packages/dd-trace/src/ci-visibility/test-screenshot.js +90 -0
  133. package/packages/dd-trace/src/config/generated-config-types.d.ts +16 -0
  134. package/packages/dd-trace/src/config/helper.js +1 -0
  135. package/packages/dd-trace/src/config/index.js +10 -0
  136. package/packages/dd-trace/src/config/supported-configurations.json +66 -0
  137. package/packages/dd-trace/src/exporter.js +2 -0
  138. package/packages/dd-trace/src/exporters/common/client-library-headers.js +21 -0
  139. package/packages/dd-trace/src/exporters/common/request.js +59 -30
  140. package/packages/dd-trace/src/exporters/common/url.js +15 -1
  141. package/packages/dd-trace/src/feature-registry.js +10 -3
  142. package/packages/dd-trace/src/llmobs/experiments/client.js +113 -0
  143. package/packages/dd-trace/src/llmobs/experiments/dataset.js +154 -0
  144. package/packages/dd-trace/src/llmobs/experiments/experiment.js +283 -0
  145. package/packages/dd-trace/src/llmobs/experiments/index.js +152 -0
  146. package/packages/dd-trace/src/llmobs/experiments/noop.js +30 -0
  147. package/packages/dd-trace/src/llmobs/experiments/result.js +34 -0
  148. package/packages/dd-trace/src/llmobs/noop.js +6 -0
  149. package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +20 -3
  150. package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +2 -2
  151. package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
  152. package/packages/dd-trace/src/llmobs/plugins/openai/constants.js +8 -0
  153. package/packages/dd-trace/src/llmobs/plugins/openai/index.js +48 -14
  154. package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +43 -0
  155. package/packages/dd-trace/src/llmobs/sdk.js +17 -0
  156. package/packages/dd-trace/src/llmobs/tagger.js +68 -0
  157. package/packages/dd-trace/src/llmobs/telemetry.js +2 -1
  158. package/packages/dd-trace/src/llmobs/util.js +32 -0
  159. package/packages/dd-trace/src/openfeature/agentless_configuration_source.js +322 -0
  160. package/packages/dd-trace/src/openfeature/configuration_source.js +90 -0
  161. package/packages/dd-trace/src/openfeature/flagging_provider.js +14 -23
  162. package/packages/dd-trace/src/openfeature/index.js +0 -2
  163. package/packages/dd-trace/src/openfeature/noop.js +1 -28
  164. package/packages/dd-trace/src/openfeature/register.js +16 -27
  165. package/packages/dd-trace/src/openfeature/remote_config.js +15 -18
  166. package/packages/dd-trace/src/openfeature/require-provider.js +18 -0
  167. package/packages/dd-trace/src/opentelemetry/span-ending-hook.js +10 -0
  168. package/packages/dd-trace/src/opentelemetry/span.js +5 -0
  169. package/packages/dd-trace/src/plugins/ci_plugin.js +20 -6
  170. package/packages/dd-trace/src/plugins/util/git.js +3 -2
  171. package/packages/dd-trace/src/plugins/util/inferred_proxy.js +29 -5
  172. package/packages/dd-trace/src/plugins/util/test.js +56 -4
  173. package/packages/dd-trace/src/profiling/profilers/space.js +1 -1
  174. package/packages/dd-trace/src/profiling/profilers/wall.js +1 -0
  175. package/packages/dd-trace/src/proxy.js +84 -7
  176. package/packages/dd-trace/src/span_processor.js +5 -0
  177. package/packages/dd-trace/src/standalone/index.js +0 -14
  178. package/packages/dd-trace/src/telemetry/send-data.js +2 -2
  179. package/packages/dd-trace/src/util.js +26 -0
  180. package/vendor/dist/@apm-js-collab/code-transformer/index.js +1 -1
  181. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  182. package/vendor/dist/protobufjs/index.js +1 -1
  183. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  184. package/vendor/dist/shell-quote/index.js +1 -1
  185. package/packages/datadog-instrumentations/src/helpers/require-optional-peer.js +0 -17
@@ -0,0 +1,327 @@
1
+ 'use strict'
2
+
3
+ const crypto = require('node:crypto')
4
+ const fs = require('node:fs')
5
+ const os = require('node:os')
6
+ const path = require('node:path')
7
+
8
+ const { getArtifactId } = require('./artifact-id')
9
+ const { createFileSafely, ensureSafeDirectory } = require('./safe-files')
10
+
11
+ const MAX_FIXTURE_FILE_BYTES = 1024 * 1024
12
+ const OFFLINE_FIXTURE_NONCE_PATTERN = /^[a-f0-9]{32}$/
13
+ const DEFAULT_SETTINGS = {
14
+ code_coverage: false,
15
+ tests_skipping: false,
16
+ itr_enabled: false,
17
+ require_git: false,
18
+ early_flake_detection: {
19
+ enabled: false,
20
+ slow_test_retries: {
21
+ '5s': 3,
22
+ },
23
+ faulty_session_threshold: 100,
24
+ },
25
+ flaky_test_retries_enabled: false,
26
+ di_enabled: false,
27
+ known_tests_enabled: false,
28
+ test_management: {
29
+ enabled: false,
30
+ },
31
+ impacted_tests_enabled: false,
32
+ coverage_report_upload_enabled: false,
33
+ }
34
+
35
+ /**
36
+ * Creates one validator-controlled cache fixture outside the repository.
37
+ *
38
+ * @param {object} input fixture inputs
39
+ * @param {string} input.approvedPlanSha256 approved execution-plan digest
40
+ * @param {string} input.offlineFixtureNonce random fixture-root nonce from the approved plan
41
+ * @param {object} input.framework framework manifest entry
42
+ * @param {string} input.repositoryRoot repository checkout root
43
+ * @param {string} input.scenarioName unique scenario execution name
44
+ * @param {object} [input.settings] scenario settings overrides
45
+ * @param {object} [input.knownTests] known-tests fixture contents
46
+ * @param {object[]} [input.skippableTests] skippable-tests fixture contents
47
+ * @param {object} [input.testManagementTests] managed-tests fixture contents
48
+ * @returns {{manifestPath: string, root: string, files: object[]}} fixture details
49
+ */
50
+ function createOfflineFixture ({
51
+ approvedPlanSha256,
52
+ offlineFixtureNonce,
53
+ framework,
54
+ repositoryRoot,
55
+ scenarioName,
56
+ settings,
57
+ knownTests = {},
58
+ skippableTests = [],
59
+ testManagementTests = {},
60
+ }) {
61
+ if (!/^[a-f0-9]{64}$/.test(approvedPlanSha256 || '')) {
62
+ throw new Error('Offline validation requires an approved plan digest before creating fixtures.')
63
+ }
64
+ if (!OFFLINE_FIXTURE_NONCE_PATTERN.test(offlineFixtureNonce || '')) {
65
+ throw new Error('Offline validation requires the fixture nonce from the approved execution plan.')
66
+ }
67
+
68
+ const { base, root } = getOfflineFixturePaths({ offlineFixtureNonce, framework, scenarioName })
69
+ if (isPathInside(path.resolve(repositoryRoot), base)) {
70
+ throw new Error('Offline validation fixtures must be outside the repository checkout.')
71
+ }
72
+ ensurePrivateDirectory(base)
73
+ if (fs.existsSync(root)) {
74
+ throw new Error(`Offline validation fixture already exists and will not be replaced: ${root}`)
75
+ }
76
+ try {
77
+ fs.mkdirSync(root, { recursive: true, mode: 0o700 })
78
+ ensureSafeDirectory(base, root, 'offline validation fixture directory')
79
+
80
+ const testOptimizationRoot = path.join(root, '.testoptimization')
81
+ const cacheRoot = path.join(testOptimizationRoot, 'cache', 'http')
82
+ fs.mkdirSync(cacheRoot, { recursive: true, mode: 0o700 })
83
+ ensureSafeDirectory(root, cacheRoot, 'offline validation cache directory')
84
+
85
+ const manifestPath = path.join(testOptimizationRoot, 'manifest.txt')
86
+ const fixtureFiles = [
87
+ [manifestPath, '1\n'],
88
+ [path.join(cacheRoot, 'settings.json'), JSON.stringify({
89
+ data: { attributes: mergeSettings(settings) },
90
+ })],
91
+ [path.join(cacheRoot, 'known_tests.json'), JSON.stringify({
92
+ data: { attributes: { tests: knownTests } },
93
+ })],
94
+ [path.join(cacheRoot, 'skippable_tests.json'), JSON.stringify({
95
+ data: skippableTests,
96
+ meta: { correlation_id: 'dd-test-optimization-validation' },
97
+ })],
98
+ [path.join(cacheRoot, 'test_management.json'), JSON.stringify({
99
+ data: { attributes: { modules: testManagementTests } },
100
+ })],
101
+ ]
102
+
103
+ for (const [filename, content] of fixtureFiles) {
104
+ if (Buffer.byteLength(content) > MAX_FIXTURE_FILE_BYTES) {
105
+ throw new Error(`Offline validation fixture exceeds ${MAX_FIXTURE_FILE_BYTES} bytes: ${filename}`)
106
+ }
107
+ createFileSafely(root, filename, content, 'offline validation fixture')
108
+ }
109
+
110
+ return {
111
+ manifestPath,
112
+ root,
113
+ files: fixtureFiles.map(([filename, content]) => ({ filename, bytes: Buffer.byteLength(content) })),
114
+ }
115
+ } catch (error) {
116
+ fs.rmSync(root, { recursive: true, force: true })
117
+ removeEmptyParents(path.dirname(root), base)
118
+ throw error
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Returns random, validator-controlled fixture paths bound to an approved execution plan.
124
+ *
125
+ * @param {object} input fixture path inputs
126
+ * @param {string} input.offlineFixtureNonce random fixture-root nonce from the approved plan
127
+ * @param {object} input.framework framework manifest entry
128
+ * @param {string} input.scenarioName scenario execution name
129
+ * @returns {{base: string, root: string}} fixture base and scenario root
130
+ */
131
+ function getOfflineFixturePaths ({ offlineFixtureNonce, framework, scenarioName }) {
132
+ if (!OFFLINE_FIXTURE_NONCE_PATTERN.test(offlineFixtureNonce || '')) {
133
+ throw new Error('Invalid offline validation fixture nonce.')
134
+ }
135
+ const base = path.join(fs.realpathSync(os.tmpdir()), `dd-test-optimization-validation-${offlineFixtureNonce}`)
136
+ return {
137
+ base,
138
+ root: path.join(base, getArtifactId(framework.id), getArtifactId(scenarioName)),
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Returns the cache executions selected by a validator scenario selection.
144
+ *
145
+ * @param {string|null|undefined} requestedScenario selected validation scenario
146
+ * @returns {string[]} cache execution names
147
+ */
148
+ function getOfflineScenarioNames (requestedScenario) {
149
+ const scenarios = new Set(['basic-reporting', 'basic-reporting-debug'])
150
+ if (!requestedScenario || requestedScenario === 'ci-wiring') scenarios.add('ci-wiring')
151
+ const advanced = requestedScenario
152
+ ? requestedScenario === 'basic-reporting' || requestedScenario === 'ci-wiring'
153
+ ? []
154
+ : [requestedScenario]
155
+ : ['efd', 'atr', 'test-management']
156
+ for (const scenario of advanced) {
157
+ scenarios.add(`${scenario}-baseline`)
158
+ scenarios.add(scenario)
159
+ scenarios.add(`${scenario}-debug`)
160
+ }
161
+ return [...scenarios]
162
+ }
163
+
164
+ /**
165
+ * Hashes the validator-controlled inputs used to build one scenario fixture.
166
+ *
167
+ * @param {object} framework framework manifest entry
168
+ * @param {string} scenarioName cache execution name
169
+ * @returns {string} SHA-256 fixture recipe digest
170
+ */
171
+ function getFixtureRecipeDigest (framework, scenarioName) {
172
+ const generatedScenarioId = {
173
+ atr: 'atr-fail-once',
174
+ efd: 'basic-pass',
175
+ 'test-management': 'test-management-target',
176
+ }[scenarioName.replace(/-(?:baseline|debug)$/, '')]
177
+ const recipe = {
178
+ version: 1,
179
+ framework: framework.framework,
180
+ scenarioName,
181
+ defaultSettings: DEFAULT_SETTINGS,
182
+ settingsOverrides: getFixtureSettingsOverrides(scenarioName),
183
+ testIdentities: framework.generatedTestStrategy?.scenarios?.find(scenario => {
184
+ return scenario.id === generatedScenarioId
185
+ })?.testIdentities || [],
186
+ dynamicTestManagementIdentity: scenarioName.startsWith('test-management'),
187
+ }
188
+ return crypto.createHash('sha256').update(JSON.stringify(recipe)).digest('hex')
189
+ }
190
+
191
+ /**
192
+ * Returns stable fixture recipe hashes included directly in the approval scope.
193
+ *
194
+ * @param {object} input recipe selection
195
+ * @param {object[]} input.frameworks normalized manifest framework entries
196
+ * @param {string[]} [input.selectedFrameworkIds] selected framework ids
197
+ * @param {string|null} [input.requestedScenario] selected validation scenario
198
+ * @returns {object[]} fixture recipe identities and hashes
199
+ */
200
+ function getFixtureRecipeDigests ({ frameworks, selectedFrameworkIds = [], requestedScenario = null }) {
201
+ const selected = new Set(selectedFrameworkIds)
202
+ const entries = []
203
+ for (const framework of frameworks) {
204
+ if (framework.status !== 'runnable' || (selected.size > 0 && !selected.has(framework.id))) continue
205
+ for (const scenarioName of getOfflineScenarioNames(requestedScenario)) {
206
+ entries.push({
207
+ frameworkId: framework.id,
208
+ scenarioName,
209
+ sha256: getFixtureRecipeDigest(framework, scenarioName),
210
+ })
211
+ }
212
+ }
213
+ return entries
214
+ }
215
+
216
+ function getFixtureSettingsOverrides (scenarioName) {
217
+ if (scenarioName === 'atr' || scenarioName === 'atr-debug') {
218
+ return { flaky_test_retries_enabled: true }
219
+ }
220
+ if (scenarioName === 'efd' || scenarioName === 'efd-debug') {
221
+ return {
222
+ early_flake_detection: {
223
+ enabled: true,
224
+ slow_test_retries: { '5s': 3 },
225
+ faulty_session_threshold: 100,
226
+ },
227
+ known_tests_enabled: true,
228
+ }
229
+ }
230
+ if (scenarioName === 'test-management' || scenarioName === 'test-management-debug') {
231
+ return { test_management: { enabled: true, attempt_to_fix_retries: 2 } }
232
+ }
233
+ return {}
234
+ }
235
+
236
+ /**
237
+ * Removes a scenario fixture after its command has exited and output has been read.
238
+ *
239
+ * @param {string} fixtureRoot scenario fixture root
240
+ */
241
+ function cleanupOfflineFixture (fixtureRoot) {
242
+ const parent = path.dirname(path.dirname(fixtureRoot))
243
+ ensureSafeDirectory(parent, fixtureRoot, 'offline validation fixture cleanup')
244
+ fs.rmSync(fixtureRoot, { recursive: true })
245
+ removeEmptyParents(path.dirname(fixtureRoot), parent)
246
+ }
247
+
248
+ function mergeSettings (settings = {}) {
249
+ return {
250
+ ...DEFAULT_SETTINGS,
251
+ ...settings,
252
+ early_flake_detection: {
253
+ ...DEFAULT_SETTINGS.early_flake_detection,
254
+ ...settings.early_flake_detection,
255
+ },
256
+ test_management: {
257
+ ...DEFAULT_SETTINGS.test_management,
258
+ ...settings.test_management,
259
+ },
260
+ }
261
+ }
262
+
263
+ /**
264
+ * Creates or verifies a private validator-owned fixture directory.
265
+ *
266
+ * @param {string} directory fixture base directory
267
+ * @returns {void}
268
+ */
269
+ function ensurePrivateDirectory (directory) {
270
+ try {
271
+ fs.mkdirSync(directory, { mode: 0o700 })
272
+ } catch (error) {
273
+ if (error.code !== 'EEXIST') throw error
274
+ }
275
+ const stat = fs.lstatSync(directory)
276
+ const supportsPosixPermissions = typeof process.getuid === 'function'
277
+ const ownerMismatch = supportsPosixPermissions && stat.uid !== process.getuid()
278
+ const permissionsMismatch = supportsPosixPermissions && (stat.mode & 0o077) !== 0
279
+ if (!stat.isDirectory() || stat.isSymbolicLink() || ownerMismatch || permissionsMismatch) {
280
+ throw new Error(`Offline validation fixture base is not a regular directory: ${directory}`)
281
+ }
282
+ }
283
+
284
+ /**
285
+ * Removes empty directories up to and including the fixture base.
286
+ *
287
+ * @param {string} directory first candidate directory
288
+ * @param {string} stop fixture base directory
289
+ * @returns {void}
290
+ */
291
+ function removeEmptyParents (directory, stop) {
292
+ let current = directory
293
+ while (current !== stop && current.startsWith(`${stop}${path.sep}`)) {
294
+ try {
295
+ fs.rmdirSync(current)
296
+ } catch {
297
+ return
298
+ }
299
+ current = path.dirname(current)
300
+ }
301
+ try {
302
+ fs.rmdirSync(stop)
303
+ } catch {}
304
+ }
305
+
306
+ /**
307
+ * Checks lexical path containment.
308
+ *
309
+ * @param {string} root candidate parent
310
+ * @param {string} filename candidate child
311
+ * @returns {boolean} whether the child is inside the parent
312
+ */
313
+ function isPathInside (root, filename) {
314
+ const relative = path.relative(root, filename)
315
+ return relative === '' || (relative && !relative.startsWith('..') && !path.isAbsolute(relative))
316
+ }
317
+
318
+ module.exports = {
319
+ cleanupOfflineFixture,
320
+ createOfflineFixture,
321
+ DEFAULT_SETTINGS,
322
+ getFixtureRecipeDigest,
323
+ getFixtureRecipeDigests,
324
+ getOfflineFixturePaths,
325
+ getOfflineScenarioNames,
326
+ MAX_FIXTURE_FILE_BYTES,
327
+ }