dd-trace 6.8.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 (179) 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 +9 -3
  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/vitest.js +517 -0
  20. package/ci/test-optimization-validation/generated-files.js +38 -28
  21. package/ci/test-optimization-validation/generated-test-contract.js +8 -4
  22. package/ci/test-optimization-validation/generated-verifier.js +24 -17
  23. package/ci/test-optimization-validation/literal-glob.js +52 -0
  24. package/ci/test-optimization-validation/manifest-loader.js +3 -0
  25. package/ci/test-optimization-validation/manifest-scaffold.js +300 -50
  26. package/ci/test-optimization-validation/manifest-schema.js +80 -17
  27. package/ci/test-optimization-validation/offline-fixtures.js +13 -4
  28. package/ci/test-optimization-validation/package-check.js +94 -0
  29. package/ci/test-optimization-validation/plan-writer.js +183 -10
  30. package/ci/test-optimization-validation/preflight-runner.js +110 -31
  31. package/ci/test-optimization-validation/project-build-artifact.js +31 -0
  32. package/ci/test-optimization-validation/redaction.js +18 -27
  33. package/ci/test-optimization-validation/report-writer.js +182 -48
  34. package/ci/test-optimization-validation/result-semantics.js +20 -3
  35. package/ci/test-optimization-validation/runner-command.js +40 -20
  36. package/ci/test-optimization-validation/runner-contract.js +79 -43
  37. package/ci/test-optimization-validation/scenarios/basic-reporting.js +59 -5
  38. package/ci/test-optimization-validation/scenarios/ci-wiring.js +141 -35
  39. package/ci/test-optimization-validation/scenarios/helpers.js +30 -6
  40. package/ci/test-optimization-validation/static-diagnosis.js +10 -3
  41. package/ci/test-optimization-validation/test-output.js +2 -1
  42. package/ci/test-optimization-validation/validation-blocker.js +21 -0
  43. package/ci/vitest-no-worker-init-setup.mjs +30 -34
  44. package/index.d.ts +98 -1
  45. package/init.js +1 -17
  46. package/initialize.mjs +11 -0
  47. package/loader-hook.mjs +6 -4
  48. package/package.json +5 -3
  49. package/packages/datadog-core/src/utils/src/parse-tags.js +0 -1
  50. package/packages/datadog-esbuild/src/utils.js +16 -1
  51. package/packages/datadog-instrumentations/src/ai.js +61 -34
  52. package/packages/datadog-instrumentations/src/bluebird.js +6 -6
  53. package/packages/datadog-instrumentations/src/claude-agent-sdk.js +81 -5
  54. package/packages/datadog-instrumentations/src/cucumber.js +124 -47
  55. package/packages/datadog-instrumentations/src/dns.js +21 -2
  56. package/packages/datadog-instrumentations/src/electron.js +1 -0
  57. package/packages/datadog-instrumentations/src/fastify.js +3 -1
  58. package/packages/datadog-instrumentations/src/grpc/client.js +4 -2
  59. package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +3 -1
  60. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
  61. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +33 -9
  62. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +118 -0
  63. package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +20 -2
  64. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.js +32 -0
  65. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +61 -0
  66. package/packages/datadog-instrumentations/src/helpers/router-helper.js +37 -6
  67. package/packages/datadog-instrumentations/src/jest/coverage-backfill.js +1 -1
  68. package/packages/datadog-instrumentations/src/jest.js +979 -233
  69. package/packages/datadog-instrumentations/src/mocha/main.js +107 -9
  70. package/packages/datadog-instrumentations/src/mocha/utils.js +64 -42
  71. package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +25 -0
  72. package/packages/datadog-instrumentations/src/mocha/worker.js +12 -18
  73. package/packages/datadog-instrumentations/src/mysql.js +1 -1
  74. package/packages/datadog-instrumentations/src/next.js +155 -23
  75. package/packages/datadog-instrumentations/src/openai.js +14 -0
  76. package/packages/datadog-instrumentations/src/pg.js +180 -6
  77. package/packages/datadog-instrumentations/src/playwright.js +173 -79
  78. package/packages/datadog-instrumentations/src/promise-js.js +4 -4
  79. package/packages/datadog-instrumentations/src/promise.js +3 -3
  80. package/packages/datadog-instrumentations/src/router.js +195 -19
  81. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +43 -10
  82. package/packages/datadog-instrumentations/src/vitest-main.js +360 -56
  83. package/packages/datadog-instrumentations/src/vitest-util.js +42 -7
  84. package/packages/datadog-instrumentations/src/vitest-worker.js +234 -17
  85. package/packages/datadog-instrumentations/src/webdriverio.js +194 -18
  86. package/packages/datadog-instrumentations/src/when.js +3 -3
  87. package/packages/datadog-instrumentations/src/ws.js +5 -1
  88. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +59 -48
  89. package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +1 -3
  90. package/packages/datadog-plugin-cucumber/src/index.js +5 -1
  91. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +50 -46
  92. package/packages/datadog-plugin-cypress/src/support.js +7 -4
  93. package/packages/datadog-plugin-electron/src/net.js +10 -4
  94. package/packages/datadog-plugin-http/src/client.js +1 -1
  95. package/packages/datadog-plugin-http2/src/client.js +1 -1
  96. package/packages/datadog-plugin-jest/src/index.js +1 -18
  97. package/packages/datadog-plugin-jest/src/util.js +2 -0
  98. package/packages/datadog-plugin-kafkajs/src/producer.js +2 -1
  99. package/packages/datadog-plugin-mocha/src/index.js +318 -1
  100. package/packages/datadog-plugin-next/src/index.js +57 -9
  101. package/packages/datadog-plugin-openai/src/tracing.js +4 -0
  102. package/packages/datadog-plugin-pg/src/index.js +65 -1
  103. package/packages/datadog-plugin-playwright/src/index.js +1 -1
  104. package/packages/datadog-plugin-vitest/src/index.js +95 -7
  105. package/packages/datadog-shimmer/src/shimmer.js +6 -2
  106. package/packages/dd-trace/src/aiguard/sdk.js +3 -0
  107. package/packages/dd-trace/src/appsec/{blocking.js → blocking/index.js} +26 -6
  108. package/packages/dd-trace/src/appsec/handlers/auth.js +62 -0
  109. package/packages/dd-trace/src/appsec/handlers/http-request.js +215 -0
  110. package/packages/dd-trace/src/appsec/handlers/http-response.js +78 -0
  111. package/packages/dd-trace/src/appsec/handlers/http-shared.js +24 -0
  112. package/packages/dd-trace/src/appsec/handlers/payments.js +104 -0
  113. package/packages/dd-trace/src/appsec/iast/analyzers/hsts-header-missing-analyzer.js +1 -1
  114. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +1 -1
  115. package/packages/dd-trace/src/appsec/index.js +48 -476
  116. package/packages/dd-trace/src/appsec/sdk/track_event.js +9 -0
  117. package/packages/dd-trace/src/ci-visibility/coverage-report-discovery.js +46 -14
  118. package/packages/dd-trace/src/ci-visibility/efd-retry-policy.js +89 -0
  119. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +27 -13
  120. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +17 -0
  121. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +21 -1
  122. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +22 -7
  123. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +9 -14
  124. package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +21 -5
  125. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +1 -1
  126. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +5 -1
  127. package/packages/dd-trace/src/config/generated-config-types.d.ts +2 -0
  128. package/packages/dd-trace/src/config/helper.js +2 -0
  129. package/packages/dd-trace/src/config/index.js +16 -5
  130. package/packages/dd-trace/src/config/remote_config.js +1 -1
  131. package/packages/dd-trace/src/config/supported-configurations.json +8 -0
  132. package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -1
  133. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +0 -1
  134. package/packages/dd-trace/src/guardrails/apply-pm2-env.js +16 -0
  135. package/packages/dd-trace/src/guardrails/log.js +1 -10
  136. package/packages/dd-trace/src/llmobs/eval-metric.js +249 -0
  137. package/packages/dd-trace/src/llmobs/experiments/index.js +33 -25
  138. package/packages/dd-trace/src/llmobs/experiments/util.js +4 -2
  139. package/packages/dd-trace/src/llmobs/noop.js +2 -0
  140. package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +1 -1
  141. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +1 -1
  142. package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +5 -5
  143. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +1 -1
  144. package/packages/dd-trace/src/llmobs/sdk.js +149 -68
  145. package/packages/dd-trace/src/llmobs/telemetry.js +18 -0
  146. package/packages/dd-trace/src/llmobs/writers/base.js +3 -0
  147. package/packages/dd-trace/src/llmobs/writers/util.js +2 -0
  148. package/packages/dd-trace/src/log/channels.js +1 -9
  149. package/packages/dd-trace/src/log/levels.js +12 -0
  150. package/packages/dd-trace/src/openfeature/writers/exposures.js +1 -1
  151. package/packages/dd-trace/src/openfeature/writers/util.js +1 -1
  152. package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +1 -1
  153. package/packages/dd-trace/src/opentelemetry/metrics/meter.js +8 -4
  154. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +1 -1
  155. package/packages/dd-trace/src/opentelemetry/span.js +4 -0
  156. package/packages/dd-trace/src/plugins/ci_plugin.js +21 -5
  157. package/packages/dd-trace/src/plugins/index.js +1 -0
  158. package/packages/dd-trace/src/plugins/util/ci.js +6 -0
  159. package/packages/dd-trace/src/plugins/util/git.js +6 -4
  160. package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +2 -2
  161. package/packages/dd-trace/src/plugins/util/ip_extractor.js +1 -1
  162. package/packages/dd-trace/src/plugins/util/stacktrace.js +3 -0
  163. package/packages/dd-trace/src/plugins/util/test.js +319 -75
  164. package/packages/dd-trace/src/plugins/util/url.js +1 -1
  165. package/packages/dd-trace/src/plugins/util/web.js +23 -1
  166. package/packages/dd-trace/src/profiler.js +1 -1
  167. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +1 -1
  168. package/packages/dd-trace/src/profiling/profilers/events.js +2 -2
  169. package/packages/dd-trace/src/proxy.js +1 -0
  170. package/packages/dd-trace/src/ritm.js +5 -0
  171. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +4 -2
  172. package/packages/dd-trace/src/sampling_rule.js +4 -2
  173. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +1 -1
  174. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +1 -1
  175. package/packages/dd-trace/src/telemetry/logs/index.js +3 -0
  176. package/packages/dd-trace/src/telemetry/telemetry.js +1 -1
  177. package/vendor/dist/pprof-format/index.js +1 -1
  178. package/version.js +10 -8
  179. /package/packages/dd-trace/src/appsec/{blocked_templates.js → blocking/templates.js} +0 -0
@@ -4,6 +4,7 @@ const fs = require('node:fs')
4
4
  const path = require('path')
5
5
 
6
6
  const { getArtifactId } = require('../artifact-id')
7
+ const { BLOCKER_CATEGORIES } = require('../blocker-category')
7
8
  const { buildDatadogEnv, buildOfflineCaptureEnv, runCommand } = require('../command-runner')
8
9
  const {
9
10
  cleanupGeneratedRuntimeFiles,
@@ -19,8 +20,10 @@ const { readOfflineOutput } = require('../offline-output')
19
20
  const { sanitizeForReport, sanitizeString } = require('../redaction')
20
21
  const { getGeneratedCommand } = require('../runner-command')
21
22
  const { ensureSafeDirectory, writeFileSafely } = require('../safe-files')
23
+ const { getValidationBlockerEvidence } = require('../validation-blocker')
22
24
 
23
- const ANSI_PATTERN = new RegExp(`${String.fromCharCode(27)}${String.raw`\[[0-?]*[ -/]*[@-~]`}`, 'g')
25
+ const ANSI_PATTERN =
26
+ new RegExp(`${String.fromCharCode(27)}${String.raw`\[[\u0030-\u003F]*[\u0020-\u002F]*[\u0040-\u007E]`}`, 'g')
24
27
 
25
28
  function frameworkOutDir (out, framework, scenario) {
26
29
  return path.join(out, 'runs', getArtifactId(framework.id), scenario)
@@ -193,10 +196,11 @@ async function prepareGeneratedScenario (framework, scenarioId) {
193
196
  function requireGeneratedScenario (framework, scenarioId, scenarioName) {
194
197
  const strategy = framework.generatedTestStrategy
195
198
  if (strategy?.status === 'not_possible') {
196
- return skip(
199
+ return incomplete(
197
200
  framework,
198
201
  scenarioName,
199
- `Skipped because this advanced feature is not eligible: ${strategy.reason}`,
202
+ `The validator cannot create a collectible test for this advanced feature: ${strategy.reason} ` +
203
+ 'No conclusion was reached for this advanced feature.',
200
204
  getGeneratedStrategySkipEvidence(framework, scenarioName, scenarioId)
201
205
  )
202
206
  }
@@ -254,9 +258,9 @@ function getGeneratedStrategySkipEvidence (framework, scenarioName, scenarioId)
254
258
  reasonCode = 'generated-test-strategy-not-verified'
255
259
  }
256
260
 
257
- return {
261
+ const evidence = {
258
262
  featureEligibility: {
259
- eligible: false,
263
+ ...(reasonCode === 'generated-test-strategy-not-possible' ? {} : { eligible: false }),
260
264
  blockedBy: 'generated-test-strategy',
261
265
  reason: strategy?.reason,
262
266
  reasonCode,
@@ -265,6 +269,12 @@ function getGeneratedStrategySkipEvidence (framework, scenarioName, scenarioId)
265
269
  requiredGeneratedScenario: scenarioId,
266
270
  },
267
271
  }
272
+ if (reasonCode === 'generated-test-strategy-not-possible') {
273
+ evidence.blockerCategory = BLOCKER_CATEGORIES.VALIDATOR_LIMITATION
274
+ evidence.recommendation = 'Report that the selected runner configuration cannot collect validator-generated ' +
275
+ 'tests to validator engineering. Basic Reporting remains valid; project setup changes are not required.'
276
+ }
277
+ return evidence
268
278
  }
269
279
 
270
280
  function basicEventEvidence (events) {
@@ -442,6 +452,8 @@ function copy (target, source, key) {
442
452
 
443
453
  function summarizeDebugRerun ({ result, events, offline, outDir }) {
444
454
  const output = `${result.stdout}\n${result.stderr}`
455
+ const testEvents = eventsOfType(events, 'test')
456
+ const sourceFiles = new Set(testEvents.map(test => test.testSourceFile).filter(Boolean))
445
457
 
446
458
  return {
447
459
  ran: true,
@@ -449,8 +461,12 @@ function summarizeDebugRerun ({ result, events, offline, outDir }) {
449
461
  commandTimedOut: result.timedOut,
450
462
  debugCommandFailed: result.exitCode !== 0 || result.timedOut === true,
451
463
  offlineExporterInitialized: offline.initialized,
464
+ settingsLoadedFromCache: offline.inputs.settings?.status === 'loaded',
452
465
  artifactDirectory: outDir,
453
466
  ...basicEventEvidence(events),
467
+ testEventsWithoutSourceFile: testEvents.length - testEvents.filter(test => test.testSourceFile).length,
468
+ testSourceFileCount: sourceFiles.size,
469
+ testSourceFiles: [...sourceFiles].slice(0, 5),
454
470
  debugLines: findInterestingLines(output, [
455
471
  /dd-trace/i,
456
472
  /test optimization/i,
@@ -541,7 +557,15 @@ function inconclusive (framework, scenario, diagnosis, evidence = {}, outDir, ex
541
557
  }
542
558
 
543
559
  function error (framework, scenario, err, outDir = err?.artifactDirectory) {
544
- return result(framework, scenario, 'error', err && err.stack ? err.stack : String(err), {}, outDir)
560
+ const blockerEvidence = getValidationBlockerEvidence(err)
561
+ return result(
562
+ framework,
563
+ scenario,
564
+ blockerEvidence ? 'blocked' : 'error',
565
+ blockerEvidence ? err.message : (err && err.stack ? err.stack : String(err)),
566
+ blockerEvidence || {},
567
+ outDir
568
+ )
545
569
  }
546
570
 
547
571
  function result (framework, scenario, status, diagnosis, evidence, outDir, extraArtifacts) {
@@ -8,6 +8,7 @@ const {
8
8
  getFrameworkDefinitions,
9
9
  runDiagnosis,
10
10
  } = require('../diagnose')
11
+ const { BLOCKER_CATEGORIES } = require('./blocker-category')
11
12
  const { sanitizeForReport } = require('./redaction')
12
13
  const { writeFileSafely } = require('./safe-files')
13
14
 
@@ -45,6 +46,7 @@ function runStaticDiagnosis ({ manifest, out }) {
45
46
  function getStaticBlocker (framework, diagnosis) {
46
47
  if (!SUPPORTED_FRAMEWORKS.has(framework.framework)) {
47
48
  return {
49
+ blockerCategory: BLOCKER_CATEGORIES.VALIDATOR_LIMITATION,
48
50
  reason: `Unsupported test framework detected: ${getUnsupportedFrameworkName(framework)}.`,
49
51
  recommendation: 'Choose Jest, Mocha, Cucumber, Cypress, Playwright, or Vitest for live validation.',
50
52
  }
@@ -56,6 +58,7 @@ function getStaticBlocker (framework, diagnosis) {
56
58
  const version = parseVersion(framework.frameworkVersion)
57
59
  if (version && !satisfies(version, definition.supportedRange)) {
58
60
  return {
61
+ blockerCategory: BLOCKER_CATEGORIES.UNSUPPORTED_VERSION,
59
62
  reason:
60
63
  `${definition.name} ${version} is not supported. Supported range is ${definition.supportedRange}.`,
61
64
  recommendation: definition.recommendation,
@@ -65,6 +68,7 @@ function getStaticBlocker (framework, diagnosis) {
65
68
  const staticVersionError = findStaticVersionError(definition, diagnosis, framework, version)
66
69
  if (staticVersionError) {
67
70
  return {
71
+ blockerCategory: BLOCKER_CATEGORIES.UNSUPPORTED_VERSION,
68
72
  reason: staticVersionError.title,
69
73
  recommendation: staticVersionError.recommendation || staticVersionError.message,
70
74
  }
@@ -117,8 +121,11 @@ function getExactFrameworkLocations (diagnosis, framework) {
117
121
  const locations = new Set()
118
122
  addRelativeFrameworkLocation(locations, diagnosis, framework.project?.packageJson)
119
123
 
120
- for (const configFile of framework.project?.configFiles || []) {
121
- addRelativeFrameworkLocation(locations, diagnosis, configFile)
124
+ const configFiles = framework.project?.configFiles
125
+ if (configFiles) {
126
+ for (const configFile of configFiles) {
127
+ addRelativeFrameworkLocation(locations, diagnosis, configFile)
128
+ }
122
129
  }
123
130
 
124
131
  return locations
@@ -149,7 +156,7 @@ function normalizeRelativePath (location) {
149
156
 
150
157
  function parseVersion (rawVersion) {
151
158
  if (typeof rawVersion !== 'string') return null
152
- const match = rawVersion.match(/\d+\.\d+\.\d+/)
159
+ const match = rawVersion.match(/(?<!\d)\d+\.\d+\.\d+/)
153
160
  return match ? match[0] : null
154
161
  }
155
162
 
@@ -4,7 +4,8 @@ const cypressAdapter = require('./framework-adapters/cypress')
4
4
  const cucumberAdapter = require('./framework-adapters/cucumber')
5
5
  const playwrightAdapter = require('./framework-adapters/playwright')
6
6
 
7
- const ANSI_PATTERN = new RegExp(`${String.fromCharCode(27)}${String.raw`\[[0-?]*[ -/]*[@-~]`}`, 'g')
7
+ const ANSI_PATTERN =
8
+ new RegExp(`${String.fromCharCode(27)}${String.raw`\[[\u0030-\u003F]*[\u0020-\u002F]*[\u0040-\u007E]`}`, 'g')
8
9
 
9
10
  /**
10
11
  * Extracts the final test count from common JavaScript test-runner summaries.
@@ -0,0 +1,21 @@
1
+ 'use strict'
2
+
3
+ function createValidationBlocker (message, { kind, recommendation, suppressReport = false }) {
4
+ const error = new Error(message)
5
+ error.validationBlocker = { kind, recommendation }
6
+ error.validationExitCode = 2
7
+ error.suppressReport = suppressReport
8
+ return error
9
+ }
10
+
11
+ function getValidationBlockerEvidence (error) {
12
+ if (!error?.validationBlocker) return
13
+ return {
14
+ blockerKind: error.validationBlocker.kind,
15
+ domain: 'validator_state',
16
+ recommendation: error.validationBlocker.recommendation,
17
+ validationIncomplete: true,
18
+ }
19
+ }
20
+
21
+ module.exports = { createValidationBlocker, getValidationBlockerEvidence }
@@ -8,12 +8,11 @@ const isNoWorkerInitActive = providedContext.isActive ?? getIsNoWorkerInitActive
8
8
  const attemptToFixTests = providedContext.attemptToFixTests || {}
9
9
  const attemptToFixRetries = providedContext.attemptToFixRetries || 0
10
10
  const disabledTests = providedContext.disabledTests || {}
11
- const earlyFlakeDetectionRetries = providedContext.earlyFlakeDetectionRetries || 0
12
- const earlyFlakeDetectionRetryThresholds = Array.isArray(providedContext.earlyFlakeDetectionRetryThresholds)
13
- ? providedContext.earlyFlakeDetectionRetryThresholds
14
- : []
15
- const earlyFlakeDetectionSlowRetries = providedContext.earlyFlakeDetectionSlowRetries || {}
16
- const hasEarlyFlakeDetectionSlowRetries = Object.keys(earlyFlakeDetectionSlowRetries).length > 0
11
+ const earlyFlakeDetectionRetryPolicy = providedContext.earlyFlakeDetectionRetryPolicy || {
12
+ durationRetryCounts: [],
13
+ schedulingRetryCount: 0,
14
+ }
15
+ const earlyFlakeDetectionRetries = earlyFlakeDetectionRetryPolicy.schedulingRetryCount
17
16
  const isEarlyFlakeDetectionEnabled = providedContext.isEarlyFlakeDetectionEnabled === true
18
17
  const knownTests = providedContext.knownTests || {}
19
18
  const modifiedFiles = providedContext.modifiedFiles || {}
@@ -110,26 +109,29 @@ if (isNoWorkerInitActive) {
110
109
  }
111
110
 
112
111
  function applyExecutionChanges (suite) {
113
- for (const task of suite?.tasks || []) {
114
- if (task.type === 'suite') {
115
- applyExecutionChanges(task)
116
- continue
117
- }
112
+ const tasks = suite?.tasks
113
+ if (tasks) {
114
+ for (const task of tasks) {
115
+ if (task.type === 'suite') {
116
+ applyExecutionChanges(task)
117
+ continue
118
+ }
118
119
 
119
- const testSuite = getTestSuite(task)
120
- const testName = getTestName(task)
121
- if (attemptToFixTests[testSuite]?.[testName]) {
122
- task.retry = 0
123
- task.repeats = attemptToFixRetries
124
- task.meta.__ddTestOptAtfRetries = attemptToFixRetries
125
- } else if (disabledTests[testSuite]?.[testName]) {
126
- task.mode = 'skip'
127
- } else if (isEarlyFlakeDetectionTest(testSuite, testName)) {
128
- task.retry = 0
129
- task.repeats = earlyFlakeDetectionRetries
130
- task.meta.__ddTestOptEfdRetries = earlyFlakeDetectionRetries
120
+ const testSuite = getTestSuite(task)
121
+ const testName = getTestName(task)
122
+ if (attemptToFixTests[testSuite]?.[testName]) {
123
+ task.retry = 0
124
+ task.repeats = attemptToFixRetries
125
+ task.meta.__ddTestOptAtfRetries = attemptToFixRetries
126
+ } else if (disabledTests[testSuite]?.[testName]) {
127
+ task.mode = 'skip'
128
+ } else if (isEarlyFlakeDetectionTest(testSuite, testName)) {
129
+ task.retry = 0
130
+ task.repeats = earlyFlakeDetectionRetries
131
+ task.meta.__ddTestOptEfdRetries = earlyFlakeDetectionRetries
132
+ }
133
+ wrapRetryCondition(task)
131
134
  }
132
- wrapRetryCondition(task)
133
135
  }
134
136
  }
135
137
 
@@ -380,7 +382,7 @@ function recordEarlyFlakeDetectionStatus (task, attemptIndex, onlyIfNewErrors) {
380
382
  earlyFlakeDetectionRetriesByTask.set(task, retryCount)
381
383
  task.repeats = retryCount
382
384
  task.meta.__ddTestOptEfdRetries = retryCount
383
- if (retryCount === 0 && hasEarlyFlakeDetectionSlowRetries) {
385
+ if (retryCount === 0) {
384
386
  task.meta.__ddTestOptEfdAbortReason = 'slow'
385
387
  }
386
388
  }
@@ -606,7 +608,7 @@ function prepareEarlyFlakeDetectionAttempt (task, attemptIndex) {
606
608
  earlyFlakeDetectionRetriesByTask.set(task, retryCount)
607
609
  task.repeats = retryCount
608
610
  task.meta.__ddTestOptEfdRetries = retryCount
609
- if (retryCount === 0 && hasEarlyFlakeDetectionSlowRetries) {
611
+ if (retryCount === 0) {
610
612
  task.meta.__ddTestOptEfdAbortReason = 'slow'
611
613
  }
612
614
  }
@@ -633,16 +635,10 @@ function prepareEarlyFlakeDetectionAttempt (task, attemptIndex) {
633
635
  }
634
636
 
635
637
  function getEarlyFlakeDetectionRetryCount (task) {
636
- if (!hasEarlyFlakeDetectionSlowRetries) {
637
- return earlyFlakeDetectionRetries
638
- }
639
-
640
638
  const executionStart = earlyFlakeDetectionStartByTask.get(task)
641
639
  const duration = executionStart === undefined ? task.result?.duration ?? 0 : now() - executionStart
642
- for (const { key, limitMs } of earlyFlakeDetectionRetryThresholds) {
643
- if (duration < limitMs) {
644
- return earlyFlakeDetectionSlowRetries[key] ?? 0
645
- }
640
+ for (const { durationLimitMs, retryCount } of earlyFlakeDetectionRetryPolicy.durationRetryCounts) {
641
+ if (duration < durationLimitMs) return retryCount
646
642
  }
647
643
  return 0
648
644
  }
package/index.d.ts CHANGED
@@ -71,7 +71,7 @@ interface Tracer extends opentracing.Tracer {
71
71
  * @param plugin The name of a built-in plugin.
72
72
  * @param config Configuration options. Can also be `false` to disable the plugin.
73
73
  */
74
- use<P extends keyof Plugins> (plugin: P, config?: Plugins[P] | boolean): this;
74
+ use<P extends tracer.PluginName> (plugin: P, config?: tracer.PluginOptions[P] | boolean): this;
75
75
 
76
76
  /**
77
77
  * Returns a reference to the current scope.
@@ -314,6 +314,9 @@ interface Plugins {
314
314
  }
315
315
 
316
316
  declare namespace tracer {
317
+ export interface PluginOptions extends Plugins {}
318
+ export type PluginName = keyof PluginOptions;
319
+
317
320
  export type SpanOptions = Omit<opentracing.SpanOptions, 'childOf'> & {
318
321
  /**
319
322
  * Set childOf to 'null' to create a root span without a parent, even when a parent span
@@ -3711,6 +3714,13 @@ declare namespace tracer {
3711
3714
  */
3712
3715
  submitEvaluation (spanContext: llmobs.ExportedLLMObsSpan, options: llmobs.EvaluationOptions): void
3713
3716
 
3717
+ /**
3718
+ * Submits end-user feedback for a span, trace, session, or customer-defined entity.
3719
+ * Exactly one target must be provided in the options.
3720
+ * @param options An object containing the label, metric type, value, submitter and target of the feedback.
3721
+ */
3722
+ submitFeedback (options: llmobs.FeedbackOptions): void
3723
+
3714
3724
 
3715
3725
  /**
3716
3726
  * Annotates all spans, including auto-instrumented spans, with the provided tags created in the context of the callback function.
@@ -3957,6 +3967,93 @@ declare namespace tracer {
3957
3967
  metadata?: { [key: string]: any }
3958
3968
  }
3959
3969
 
3970
+ interface FeedbackSubmitter {
3971
+ /**
3972
+ * The identifier of the end user who submitted the feedback.
3973
+ */
3974
+ id: string,
3975
+
3976
+ /**
3977
+ * The type of submitter, e.g. 'user'.
3978
+ */
3979
+ type?: string
3980
+ }
3981
+
3982
+ interface FeedbackOptions {
3983
+ /**
3984
+ * The name of the feedback metric
3985
+ */
3986
+ label: string,
3987
+
3988
+ /**
3989
+ * The type of feedback metric, one of 'categorical', 'score', 'boolean', 'json' or 'text'
3990
+ */
3991
+ metricType: 'categorical' | 'score' | 'boolean' | 'json' | 'text',
3992
+
3993
+ /**
3994
+ * The value of the feedback metric.
3995
+ * Must be string for 'categorical' and 'text' metrics, number for 'score' metrics, boolean for 'boolean' metrics and a JSON object for 'json' metrics.
3996
+ */
3997
+ value: string | number | boolean | { [key: string]: any },
3998
+
3999
+ /**
4000
+ * Who submitted the feedback.
4001
+ */
4002
+ submitter: llmobs.FeedbackSubmitter,
4003
+
4004
+ /**
4005
+ * The span context of the span to attach the feedback to, as returned by `llmobs.exportSpan()`.
4006
+ * Exactly one of `span`, `spanId`, `traceId`, `sessionId` or `feedbackJoinKey` must be provided.
4007
+ */
4008
+ span?: llmobs.ExportedLLMObsSpan,
4009
+
4010
+ /**
4011
+ * The ID of the span to attach the feedback to.
4012
+ */
4013
+ spanId?: string,
4014
+
4015
+ /**
4016
+ * The ID of the trace to attach the feedback to.
4017
+ */
4018
+ traceId?: string,
4019
+
4020
+ /**
4021
+ * The ID of the session to attach the feedback to.
4022
+ */
4023
+ sessionId?: string,
4024
+
4025
+ /**
4026
+ * A customer-defined key to attach the feedback to.
4027
+ */
4028
+ feedbackJoinKey?: string,
4029
+
4030
+ /**
4031
+ * An object of string key-value pairs to tag the feedback with.
4032
+ * A `null` or `undefined` value is sent as the string `"null"` or `"undefined"`.
4033
+ */
4034
+ tags?: { [key: string]: string | null | undefined },
4035
+
4036
+ /**
4037
+ * The name of the ML application
4038
+ */
4039
+ mlApp?: string,
4040
+
4041
+ /**
4042
+ * The timestamp in milliseconds when the feedback was generated.
4043
+ */
4044
+ timestampMs?: number,
4045
+
4046
+ /**
4047
+ * Reasoning for the feedback.
4048
+ */
4049
+ reasoning?: string,
4050
+
4051
+ /**
4052
+ * Whether the feedback passed or failed. Valid values are pass and fail.
4053
+ */
4054
+ assessment?: 'pass' | 'fail'
4055
+ }
4056
+
3960
4057
  interface Document {
3961
4058
  /**
3962
4059
  * Document text
package/init.js CHANGED
@@ -1,22 +1,6 @@
1
1
  'use strict'
2
2
 
3
- // In PM2 cluster mode, per-app env vars arrive as a `pm2_env`
4
- // JSON string after --require has already run so we extract them
5
- // manually if present.
6
- var pm2EnvStr = process.env.pm2_env
7
- if (typeof pm2EnvStr === 'string') {
8
- try {
9
- var pm2Config = JSON.parse(pm2EnvStr)
10
- var pm2Keys = Object.keys(pm2Config)
11
- for (var i = 0; i < pm2Keys.length; i++) {
12
- var k = pm2Keys[i]
13
- var v = pm2Config[k]
14
- if (v != null) {
15
- process.env[k] = String(v)
16
- }
17
- }
18
- } catch (e) {}
19
- }
3
+ require('./packages/dd-trace/src/guardrails/apply-pm2-env')
20
4
 
21
5
  var guard = require('./packages/dd-trace/src/guardrails')
22
6
 
package/initialize.mjs CHANGED
@@ -20,6 +20,7 @@ import { isMainThread } from 'worker_threads'
20
20
 
21
21
  import {
22
22
  iitmExclusionRegExp,
23
+ initialize as hookInitialize,
23
24
  load as hookLoad,
24
25
  resolve as hookResolve,
25
26
  } from './loader-hook.mjs?initialize'
@@ -66,7 +67,17 @@ const [NODE_MAJOR, NODE_MINOR] = process.versions.node.split('.').map(Number)
66
67
 
67
68
  const brokenLoaders = NODE_MAJOR === 18 && NODE_MINOR === 0
68
69
 
70
+ // Node calls `initialize` only through `module.register`, never for `--loader`; without it
71
+ // import-in-the-middle keeps its default matcher and proxies every application module. Loaders
72
+ // before Node 18.19 share the application thread, where `loader-hook.mjs` builds no matcher.
73
+ let needsHookInitialize = !isMainThread && !brokenLoaders
74
+
69
75
  export async function load (url, context, nextLoad) {
76
+ if (needsHookInitialize) {
77
+ needsHookInitialize = false
78
+ hookInitialize()
79
+ }
80
+
70
81
  const loadHook = (brokenLoaders || iitmExclusionRegExp.test(url)) ? nextLoad : hookLoad
71
82
  return insertInit(await loadHook(url, context, nextLoad), url, context)
72
83
  }
package/loader-hook.mjs CHANGED
@@ -16,7 +16,6 @@ const require = Module.createRequire(import.meta.url)
16
16
  const isInitializeMainThread = isMainThread && import.meta.url.endsWith('?initialize')
17
17
  let syncImportInTheMiddleHook
18
18
 
19
- let getValueFromEnvSources
20
19
  let regexpEscape
21
20
 
22
21
  // Substrings of resolved URLs that import-in-the-middle must never wrap: re-export
@@ -32,13 +31,12 @@ const includeModuleNames = new Set()
32
31
  let moduleNameAlternation = ''
33
32
 
34
33
  if (!isInitializeMainThread) {
34
+ require('./packages/dd-trace/src/guardrails/apply-pm2-env.js')
35
35
  const regexpEscapeModule = require('./vendor/dist/escape-string-regexp/index.js')
36
36
  const hooks = require('./packages/datadog-instrumentations/src/helpers/hooks.js')
37
- const configHelper = require('./packages/dd-trace/src/config/helper.js')
38
37
  const { isRelativeRequire } = require('./packages/datadog-instrumentations/src/helpers/shared-utils.js')
39
38
 
40
39
  regexpEscape = regexpEscapeModule.default
41
- getValueFromEnvSources = configHelper.getValueFromEnvSources
42
40
 
43
41
  for (const moduleName of Object.keys(hooks)) {
44
42
  // Relative hooks resolve outside node_modules and are not instrumented here.
@@ -63,7 +61,11 @@ function prepareImportInTheMiddleOptions (data = {}) {
63
61
  // A consumer-owned shouldInclude predicate takes over the wrapping decision, so
64
62
  // iitm ignores the include/exclude arrays. Building the matcher here keeps the
65
63
  // synchronous and asynchronous loaders on one matching implementation.
66
- data.shouldInclude = createShouldInclude(getValueFromEnvSources('DD_IAST_SECURITY_CONTROLS_CONFIGURATION'))
64
+ // Lazily required and read without its registered default so a loader worker
65
+ // never loads the configuration defaults table. The default is unset regardless.
66
+ const { getValueFromEnvSources } = require('./packages/dd-trace/src/config/helper.js')
67
+
68
+ data.shouldInclude = createShouldInclude(getValueFromEnvSources('DD_IAST_SECURITY_CONTROLS_CONFIGURATION', true))
67
69
 
68
70
  return data
69
71
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dd-trace",
3
- "version": "6.8.0",
3
+ "version": "6.9.0",
4
4
  "description": "Datadog APM tracing client for JavaScript",
5
5
  "main": "index.js",
6
6
  "typings": "index.d.ts",
@@ -15,6 +15,8 @@
15
15
  "generate:electron-package": "node scripts/generate-electron-package.js",
16
16
  "verify:config:types": "node scripts/generate-config-types.js --check",
17
17
  "verify:electron-package": "node scripts/generate-electron-package.js --check",
18
+ "generate:rewriter:targets": "node scripts/generate-rewriter-targets.js",
19
+ "verify:rewriter:targets": "node scripts/generate-rewriter-targets.js --check",
18
20
  "generate:supported-integrations": "node scripts/generate-supported-integrations.js",
19
21
  "verify:supported-integrations": "node scripts/generate-supported-integrations.js --check",
20
22
  "type:check": "tsc --noEmit -p tsconfig.dev.json",
@@ -24,7 +26,7 @@
24
26
  "lint:fix": "node scripts/check_licenses.js && node scripts/check-no-coverage-artifacts.js && node scripts/check-no-mcr-images.js && node scripts/check-docker-image-shas.js && eslint . --concurrency=auto --max-warnings 0 --fix",
25
27
  "lint:inspect": "npx @eslint/config-inspector@latest",
26
28
  "lint:codeowners": "codeowners-audit",
27
- "lint:codeowners:ci": "codeowners-audit --glob='**/*.spec.js' --glob='benchmark/sirun/**' --glob='.agents/**' --glob='.claude/**' --glob='integration-tests/**'",
29
+ "lint:codeowners:ci": "codeowners-audit --glob='**/*.spec.js' --glob='benchmark/sirun/**' --glob='.agents/**' --glob='.claude/**' --glob='integration-tests/**' --glob='packages/dd-trace/src/guardrails/**'",
28
30
  "lint:editorconfig": "docker build -q -t ec .github/editorconfig-checker && docker run --rm --volume=$PWD:/check ec",
29
31
  "release:proposal": "node scripts/release/proposal",
30
32
  "services": "node ./scripts/install_plugin_modules && node packages/dd-trace/test/setup/services",
@@ -217,7 +219,7 @@
217
219
  "eslint-plugin-n": "^18.2.1",
218
220
  "eslint-plugin-promise": "^7.3.0",
219
221
  "eslint-plugin-sonarjs": "^4.2.0",
220
- "eslint-plugin-unicorn": "^72.0.0",
222
+ "eslint-plugin-unicorn": "^73.0.0",
221
223
  "express": "^5.1.0",
222
224
  "glob": "^10.4.5",
223
225
  "globals": "^17.7.0",
@@ -9,7 +9,6 @@ const digitRegex = /^\d+$/
9
9
  * { 'a.0.b': 'value' } -> { a: [{ b: 'value' }] }
10
10
  *
11
11
  * @param {object} tags - Key/value pairs of tags
12
- * @returns Object - Parsed tags
13
12
  */
14
13
  module.exports = function parseTags (tags) {
15
14
  const parsedTags = {}
@@ -85,10 +85,25 @@ function getSource (url, { format }) {
85
85
  }
86
86
  }
87
87
 
88
+ /**
89
+ * @typedef {[typeof LOAD_OPERATION, URL, object] | [typeof RESOLVE_OPERATION, string, object]} GetExportsOperation
90
+ */
91
+
92
+ /**
93
+ * @typedef {{ done: false, value: GetExportsOperation } | { done: true, value: Set<string> }} GetExportsResult
94
+ */
95
+
96
+ /**
97
+ * @typedef {{
98
+ * next: (value?: unknown) => GetExportsResult,
99
+ * throw: (error?: unknown) => GetExportsResult,
100
+ * }} GetExportsGenerator
101
+ */
102
+
88
103
  /**
89
104
  * Drives the generator returned by import-in-the-middle >=3.1.0 export discovery.
90
105
  *
91
- * @param {Generator<Array, Set<string>>} exportsGenerator Generator returned by getExports
106
+ * @param {GetExportsGenerator} exportsGenerator Generator returned by getExports
92
107
  * @param {(url: URL, context: object) => { source: string, format: string }} getSource
93
108
  * Function that loads module source
94
109
  * @returns {Set<string>}