dd-trace 5.93.0 → 5.95.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 (59) hide show
  1. package/LICENSE-3rdparty.csv +46 -44
  2. package/index.d.ts +182 -13
  3. package/package.json +14 -10
  4. package/packages/datadog-instrumentations/src/anthropic.js +1 -1
  5. package/packages/datadog-instrumentations/src/helpers/bundler-register.js +23 -0
  6. package/packages/datadog-instrumentations/src/helpers/rewriter/{orchestrion/compiler.js → compiler.js} +4 -13
  7. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +16 -2
  8. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/langgraph.js +2 -2
  9. package/packages/datadog-instrumentations/src/helpers/rewriter/{orchestrion/transforms.js → transforms.js} +3 -89
  10. package/packages/datadog-instrumentations/src/jest.js +118 -32
  11. package/packages/datadog-instrumentations/src/mocha/main.js +6 -0
  12. package/packages/datadog-instrumentations/src/mocha/utils.js +89 -5
  13. package/packages/datadog-instrumentations/src/playwright.js +10 -0
  14. package/packages/datadog-instrumentations/src/vitest.js +119 -0
  15. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +12 -0
  16. package/packages/datadog-plugin-dd-trace-api/src/index.js +1 -4
  17. package/packages/datadog-plugin-jest/src/index.js +6 -0
  18. package/packages/datadog-plugin-mocha/src/index.js +11 -0
  19. package/packages/datadog-plugin-playwright/src/index.js +9 -0
  20. package/packages/datadog-plugin-vitest/src/index.js +9 -0
  21. package/packages/datadog-webpack/index.js +187 -0
  22. package/packages/datadog-webpack/src/loader.js +27 -0
  23. package/packages/datadog-webpack/src/log.js +32 -0
  24. package/packages/dd-trace/src/azure_metadata.js +15 -15
  25. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +176 -33
  26. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +10 -21
  27. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +76 -1
  28. package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +259 -0
  29. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +56 -0
  30. package/packages/dd-trace/src/config/config-base.d.ts +7 -0
  31. package/packages/dd-trace/src/config/config-base.js +5 -0
  32. package/packages/dd-trace/src/config/config-types.d.ts +78 -0
  33. package/packages/dd-trace/src/config/generated-config-types.d.ts +582 -0
  34. package/packages/dd-trace/src/config/supported-configurations.json +7 -0
  35. package/packages/dd-trace/src/llmobs/constants/tags.js +1 -0
  36. package/packages/dd-trace/src/llmobs/constants/text.js +1 -1
  37. package/packages/dd-trace/src/llmobs/constants/writers.js +1 -1
  38. package/packages/dd-trace/src/llmobs/plugins/anthropic.js +11 -2
  39. package/packages/dd-trace/src/llmobs/plugins/openai/index.js +4 -1
  40. package/packages/dd-trace/src/llmobs/writers/spans.js +1 -1
  41. package/packages/dd-trace/src/opentracing/span.js +5 -0
  42. package/packages/dd-trace/src/plugin_manager.js +10 -7
  43. package/packages/dd-trace/src/plugins/util/test.js +76 -0
  44. package/packages/dd-trace/src/priority_sampler.js +1 -1
  45. package/packages/dd-trace/src/profiling/profilers/wall.js +35 -28
  46. package/packages/dd-trace/src/rate_limiter.js +2 -1
  47. package/packages/dd-trace/src/tagger.js +31 -35
  48. package/vendor/dist/@apm-js-collab/code-transformer/LICENSE +28 -0
  49. package/vendor/dist/@apm-js-collab/code-transformer/index.js +133 -0
  50. package/vendor/dist/@opentelemetry/core/index.js +1 -1
  51. package/vendor/dist/@opentelemetry/resources/index.js +1 -1
  52. package/vendor/dist/esquery/index.js +1 -1
  53. package/vendor/dist/meriyah/index.js +1 -1
  54. package/webpack.js +3 -0
  55. package/packages/datadog-instrumentations/src/helpers/rewriter/orchestrion/index.js +0 -43
  56. package/packages/datadog-instrumentations/src/helpers/rewriter/orchestrion/matcher.js +0 -49
  57. package/packages/datadog-instrumentations/src/helpers/rewriter/orchestrion/transformer.js +0 -121
  58. package/vendor/dist/astring/LICENSE +0 -19
  59. package/vendor/dist/astring/index.js +0 -1
@@ -5,10 +5,9 @@
5
5
  const os = require('os')
6
6
  const {
7
7
  getEnvironmentVariable,
8
- getEnvironmentVariables,
9
8
  getValueFromEnvSources,
10
9
  } = require('./config/helper')
11
- const { getIsAzureFunction, getIsFlexConsumptionAzureFunction } = require('./serverless')
10
+ const { getIsAzureFunction } = require('./serverless')
12
11
 
13
12
  function extractSubscriptionID (ownerName) {
14
13
  if (ownerName !== undefined) {
@@ -46,32 +45,33 @@ function trimObject (obj) {
46
45
  }
47
46
 
48
47
  function buildMetadata () {
49
- const {
50
- COMPUTERNAME,
51
- FUNCTIONS_EXTENSION_VERSION,
52
- FUNCTIONS_WORKER_RUNTIME,
53
- FUNCTIONS_WORKER_RUNTIME_VERSION,
54
- WEBSITE_INSTANCE_ID,
55
- WEBSITE_OWNER_NAME,
56
- WEBSITE_OS,
57
- WEBSITE_RESOURCE_GROUP,
58
- WEBSITE_SITE_NAME,
59
- } = getEnvironmentVariables()
48
+ const COMPUTERNAME = getEnvironmentVariable('COMPUTERNAME')
49
+ const FUNCTIONS_EXTENSION_VERSION = getEnvironmentVariable('FUNCTIONS_EXTENSION_VERSION')
50
+ const FUNCTIONS_WORKER_RUNTIME = getEnvironmentVariable('FUNCTIONS_WORKER_RUNTIME')
51
+ const FUNCTIONS_WORKER_RUNTIME_VERSION = getEnvironmentVariable('FUNCTIONS_WORKER_RUNTIME_VERSION')
52
+ const WEBSITE_INSTANCE_ID = getEnvironmentVariable('WEBSITE_INSTANCE_ID')
53
+ const WEBSITE_OWNER_NAME = getEnvironmentVariable('WEBSITE_OWNER_NAME')
54
+ const WEBSITE_OS = getEnvironmentVariable('WEBSITE_OS')
55
+ const WEBSITE_RESOURCE_GROUP = getEnvironmentVariable('WEBSITE_RESOURCE_GROUP')
56
+ const WEBSITE_SITE_NAME = getEnvironmentVariable('WEBSITE_SITE_NAME')
57
+ const WEBSITE_SKU = getEnvironmentVariable('WEBSITE_SKU')
60
58
 
61
59
  const DD_AZURE_RESOURCE_GROUP = getValueFromEnvSources('DD_AZURE_RESOURCE_GROUP')
60
+ const isAzureFunction = FUNCTIONS_EXTENSION_VERSION !== undefined && FUNCTIONS_WORKER_RUNTIME !== undefined
61
+ const isFlexConsumptionAzureFunction = isAzureFunction && WEBSITE_SKU === 'FlexConsumption'
62
62
 
63
63
  const subscriptionID = extractSubscriptionID(WEBSITE_OWNER_NAME)
64
64
 
65
65
  const siteName = WEBSITE_SITE_NAME
66
66
 
67
- const [siteKind, siteType] = getIsAzureFunction()
67
+ const [siteKind, siteType] = isAzureFunction
68
68
  ? ['functionapp', 'function']
69
69
  : ['app', 'app']
70
70
 
71
71
  // Azure Functions on Flex Consumption plans require the `DD_AZURE_RESOURCE_GROUP` env var.
72
72
  // If this logic ever changes, update the logic in `libdatadog`, `serverless-components/src/datadog-trace-agent`,
73
73
  // and the serverless compat layers accordingly.
74
- const resourceGroup = getIsFlexConsumptionAzureFunction()
74
+ const resourceGroup = isFlexConsumptionAzureFunction
75
75
  ? (DD_AZURE_RESOURCE_GROUP ?? WEBSITE_RESOURCE_GROUP ?? extractResourceGroup(WEBSITE_OWNER_NAME))
76
76
  : (WEBSITE_RESOURCE_GROUP ?? extractResourceGroup(WEBSITE_OWNER_NAME))
77
77
 
@@ -16,6 +16,41 @@ const {
16
16
  } = require('../../ci-visibility/telemetry')
17
17
 
18
18
  const { getNumFromKnownTests } = require('../../plugins/util/test')
19
+ const { buildCacheKey, writeToCache, withCache } = require('../requests/fs-cache')
20
+
21
+ const MAX_KNOWN_TESTS_PAGES = 10_000
22
+
23
+ /**
24
+ * Deep-merges page tests into aggregate.
25
+ * Structure: { module: { suite: [testName, ...] } }
26
+ *
27
+ * @param {object | null} aggregate
28
+ * @param {object | null} page
29
+ * @returns {object | null}
30
+ */
31
+ function mergeKnownTests (aggregate, page) {
32
+ if (!page) return aggregate
33
+ if (!aggregate) return page
34
+
35
+ for (const [moduleName, suites] of Object.entries(page)) {
36
+ if (!suites) continue
37
+
38
+ if (!aggregate[moduleName]) {
39
+ aggregate[moduleName] = suites
40
+ continue
41
+ }
42
+
43
+ for (const [suiteName, tests] of Object.entries(suites)) {
44
+ if (!tests || tests.length === 0) continue
45
+
46
+ aggregate[moduleName][suiteName] = aggregate[moduleName][suiteName]
47
+ ? [...aggregate[moduleName][suiteName], ...tests]
48
+ : tests
49
+ }
50
+ }
51
+
52
+ return aggregate
53
+ }
19
54
 
20
55
  function getKnownTests ({
21
56
  url,
@@ -32,6 +67,71 @@ function getKnownTests ({
32
67
  runtimeName,
33
68
  runtimeVersion,
34
69
  custom,
70
+ }, done) {
71
+ const cacheKey = buildCacheKey('known-tests', [
72
+ sha, service, env, repositoryUrl, osPlatform, osVersion, osArchitecture,
73
+ runtimeName, runtimeVersion, custom,
74
+ ])
75
+
76
+ withCache(cacheKey, (activeCacheKey, cb) => {
77
+ fetchFromApi({
78
+ url,
79
+ isEvpProxy,
80
+ evpProxyPrefix,
81
+ isGzipCompatible,
82
+ env,
83
+ service,
84
+ repositoryUrl,
85
+ sha,
86
+ osVersion,
87
+ osPlatform,
88
+ osArchitecture,
89
+ runtimeName,
90
+ runtimeVersion,
91
+ custom,
92
+ cacheKey: activeCacheKey,
93
+ }, cb)
94
+ }, done)
95
+ }
96
+
97
+ /**
98
+ * Fetches known tests from the API with cursor-based pagination and writes the
99
+ * result to cache on success.
100
+ *
101
+ * @param {object} params
102
+ * @param {string} params.url
103
+ * @param {boolean} params.isEvpProxy
104
+ * @param {string} params.evpProxyPrefix
105
+ * @param {boolean} params.isGzipCompatible
106
+ * @param {string} params.env
107
+ * @param {string} params.service
108
+ * @param {string} params.repositoryUrl
109
+ * @param {string} params.sha
110
+ * @param {string} params.osVersion
111
+ * @param {string} params.osPlatform
112
+ * @param {string} params.osArchitecture
113
+ * @param {string} params.runtimeName
114
+ * @param {string} params.runtimeVersion
115
+ * @param {object} [params.custom]
116
+ * @param {string | null} params.cacheKey
117
+ * @param {Function} done
118
+ */
119
+ function fetchFromApi ({
120
+ url,
121
+ isEvpProxy,
122
+ evpProxyPrefix,
123
+ isGzipCompatible,
124
+ env,
125
+ service,
126
+ repositoryUrl,
127
+ sha,
128
+ osVersion,
129
+ osPlatform,
130
+ osArchitecture,
131
+ runtimeName,
132
+ runtimeVersion,
133
+ custom,
134
+ cacheKey,
35
135
  }, done) {
36
136
  const options = {
37
137
  path: '/api/v2/ci/libraries/tests',
@@ -59,53 +159,96 @@ function getKnownTests ({
59
159
  options.headers['dd-api-key'] = apiKey
60
160
  }
61
161
 
62
- const data = JSON.stringify({
63
- data: {
64
- id: id().toString(10),
65
- type: 'ci_app_libraries_tests_request',
66
- attributes: {
67
- configurations: {
68
- 'os.platform': osPlatform,
69
- 'os.version': osVersion,
70
- 'os.architecture': osArchitecture,
71
- 'runtime.name': runtimeName,
72
- 'runtime.version': runtimeVersion,
73
- custom,
74
- },
75
- service,
76
- env,
77
- repository_url: repositoryUrl,
78
- sha,
79
- },
80
- },
81
- })
162
+ const configurations = {
163
+ 'os.platform': osPlatform,
164
+ 'os.version': osVersion,
165
+ 'os.architecture': osArchitecture,
166
+ 'runtime.name': runtimeName,
167
+ 'runtime.version': runtimeVersion,
168
+ custom,
169
+ }
82
170
 
83
171
  incrementCountMetric(TELEMETRY_KNOWN_TESTS)
84
172
 
85
173
  const startTime = Date.now()
174
+ let aggregateTests = null
175
+ let totalResponseBytes = 0
176
+ let pageNumber = 0
177
+
178
+ function fetchPage (pageState) {
179
+ pageNumber++
180
+
181
+ if (pageNumber > MAX_KNOWN_TESTS_PAGES) {
182
+ log.error('Known tests pagination exceeded maximum of %d pages. Aborting.', MAX_KNOWN_TESTS_PAGES)
183
+ distributionMetric(TELEMETRY_KNOWN_TESTS_MS, {}, Date.now() - startTime)
184
+ return done(new Error(`Known tests pagination exceeded maximum of ${MAX_KNOWN_TESTS_PAGES} pages`))
185
+ }
186
+
187
+ const pageInfo = pageState ? { page_state: pageState } : {}
188
+
189
+ const data = JSON.stringify({
190
+ data: {
191
+ id: id().toString(10),
192
+ type: 'ci_app_libraries_tests_request',
193
+ attributes: {
194
+ configurations,
195
+ service,
196
+ env,
197
+ repository_url: repositoryUrl,
198
+ sha,
199
+ page_info: pageInfo,
200
+ },
201
+ },
202
+ })
203
+
204
+ request(data, options, (err, res, statusCode) => {
205
+ if (err) {
206
+ distributionMetric(TELEMETRY_KNOWN_TESTS_MS, {}, Date.now() - startTime)
207
+ incrementCountMetric(TELEMETRY_KNOWN_TESTS_ERRORS, { statusCode })
208
+ return done(err)
209
+ }
86
210
 
87
- request(data, options, (err, res, statusCode) => {
88
- distributionMetric(TELEMETRY_KNOWN_TESTS_MS, {}, Date.now() - startTime)
89
- if (err) {
90
- incrementCountMetric(TELEMETRY_KNOWN_TESTS_ERRORS, { statusCode })
91
- done(err)
92
- } else {
93
211
  try {
94
- const { data: { attributes: { tests: knownTests } } } = JSON.parse(res)
212
+ totalResponseBytes += res.length
213
+
214
+ const { data: { attributes } } = JSON.parse(res)
215
+ const { tests: pageTests, page_info: responsePageInfo } = attributes
216
+
217
+ aggregateTests = mergeKnownTests(aggregateTests, pageTests)
218
+
219
+ // Check if there are more pages
220
+ if (responsePageInfo && responsePageInfo.has_next) {
221
+ if (!responsePageInfo.cursor) {
222
+ log.error(
223
+ 'Known tests response has has_next=true but no cursor on page %d. Aborting pagination.', pageNumber
224
+ )
225
+ distributionMetric(TELEMETRY_KNOWN_TESTS_MS, {}, Date.now() - startTime)
226
+ return done(new Error('Known tests pagination: has_next=true but no cursor'))
227
+ }
228
+ return fetchPage(responsePageInfo.cursor)
229
+ }
95
230
 
96
- const numTests = getNumFromKnownTests(knownTests)
231
+ // Done no more pages
232
+ distributionMetric(TELEMETRY_KNOWN_TESTS_MS, {}, Date.now() - startTime)
233
+
234
+ const numTests = getNumFromKnownTests(aggregateTests)
97
235
 
98
236
  distributionMetric(TELEMETRY_KNOWN_TESTS_RESPONSE_TESTS, {}, numTests)
99
- distributionMetric(TELEMETRY_KNOWN_TESTS_RESPONSE_BYTES, {}, res.length)
237
+ distributionMetric(TELEMETRY_KNOWN_TESTS_RESPONSE_BYTES, {}, totalResponseBytes)
238
+
239
+ log.debug('Number of received known tests: %d', numTests)
100
240
 
101
- log.debug('Number of received known tests:', numTests)
241
+ writeToCache(cacheKey, aggregateTests)
102
242
 
103
- done(null, knownTests)
243
+ done(null, aggregateTests)
104
244
  } catch (err) {
245
+ distributionMetric(TELEMETRY_KNOWN_TESTS_MS, {}, Date.now() - startTime)
105
246
  done(err)
106
247
  }
107
- }
108
- })
248
+ })
249
+ }
250
+
251
+ fetchPage(null)
109
252
  }
110
253
 
111
254
  module.exports = { getKnownTests }
@@ -2,10 +2,6 @@
2
2
  const { JSONEncoder } = require('../../encode/json-encoder')
3
3
  const { getEnvironmentVariable } = require('../../../config/helper')
4
4
  const log = require('../../../log')
5
- const {
6
- VITEST_WORKER_TRACE_PAYLOAD_CODE,
7
- VITEST_WORKER_LOGS_PAYLOAD_CODE,
8
- } = require('../../../plugins/util/test')
9
5
 
10
6
  class Writer {
11
7
  constructor (interprocessCode) {
@@ -29,12 +25,6 @@ class Writer {
29
25
  }
30
26
 
31
27
  _sendPayload (data, onDone = () => {}) {
32
- // ## Jest
33
- // Only available when `child_process` is used for the jest worker.
34
- // If worker_threads is used, this will not work
35
- // TODO: make `jest` instrumentation compatible with worker_threads
36
- // https://github.com/facebook/jest/blob/bb39cb2c617a3334bf18daeca66bd87b7ccab28b/packages/jest-worker/README.md#experimental-worker
37
-
38
28
  // ## Cucumber
39
29
  // This reports to the test's main process the same way test data is reported by Cucumber
40
30
  // See cucumber code:
@@ -47,19 +37,17 @@ class Writer {
47
37
  ? { __tinypool_worker_message__: true, interprocessCode: this._interprocessCode, data }
48
38
  : [this._interprocessCode, data]
49
39
 
50
- const isVitestTestWorker =
51
- this._interprocessCode === VITEST_WORKER_TRACE_PAYLOAD_CODE ||
52
- this._interprocessCode === VITEST_WORKER_LOGS_PAYLOAD_CODE
53
-
40
+ // child_process workers (jest default, cucumber)
54
41
  if (process.send) {
55
42
  process.send(payload, () => {
56
43
  onDone()
57
44
  })
58
- } else if (isVitestTestWorker) { // TODO: worker_threads are only supported in vitest right now
59
- const { isMainThread, parentPort } = require('worker_threads')
60
- if (isMainThread) {
61
- return onDone()
62
- }
45
+ return
46
+ }
47
+
48
+ // worker_threads (jest --workerThreads, vitest)
49
+ const { isMainThread, parentPort } = require('node:worker_threads')
50
+ if (!isMainThread && parentPort) {
63
51
  try {
64
52
  parentPort.postMessage(payload)
65
53
  } catch (error) {
@@ -67,9 +55,10 @@ class Writer {
67
55
  } finally {
68
56
  onDone()
69
57
  }
70
- } else {
71
- onDone()
58
+ return
72
59
  }
60
+
61
+ onDone()
73
62
  }
74
63
  }
75
64
 
@@ -13,6 +13,7 @@ const {
13
13
  TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_TESTS,
14
14
  TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_BYTES,
15
15
  } = require('../../ci-visibility/telemetry')
16
+ const { buildCacheKey, writeToCache, withCache } = require('../requests/fs-cache')
16
17
 
17
18
  function getSkippableSuites ({
18
19
  url,
@@ -30,6 +31,76 @@ function getSkippableSuites ({
30
31
  runtimeVersion,
31
32
  custom,
32
33
  testLevel = 'suite',
34
+ }, done) {
35
+ const cacheKey = buildCacheKey('skippable', [
36
+ sha, service, env, repositoryUrl, osPlatform, osVersion, osArchitecture,
37
+ runtimeName, runtimeVersion, testLevel, custom,
38
+ ])
39
+
40
+ withCache(cacheKey, (activeCacheKey, cb) => {
41
+ fetchFromApi({
42
+ url,
43
+ isEvpProxy,
44
+ evpProxyPrefix,
45
+ isGzipCompatible,
46
+ env,
47
+ service,
48
+ repositoryUrl,
49
+ sha,
50
+ osVersion,
51
+ osPlatform,
52
+ osArchitecture,
53
+ runtimeName,
54
+ runtimeVersion,
55
+ custom,
56
+ testLevel,
57
+ cacheKey: activeCacheKey,
58
+ }, cb)
59
+ }, (err, data) => {
60
+ if (err) return done(err)
61
+ done(null, data.skippableSuites, data.correlationId)
62
+ })
63
+ }
64
+
65
+ /**
66
+ * Fetches skippable suites from the API and writes the result to cache on success.
67
+ *
68
+ * @param {object} params
69
+ * @param {string} params.url
70
+ * @param {boolean} params.isEvpProxy
71
+ * @param {string} params.evpProxyPrefix
72
+ * @param {boolean} params.isGzipCompatible
73
+ * @param {string} params.env
74
+ * @param {string} params.service
75
+ * @param {string} params.repositoryUrl
76
+ * @param {string} params.sha
77
+ * @param {string} params.osVersion
78
+ * @param {string} params.osPlatform
79
+ * @param {string} params.osArchitecture
80
+ * @param {string} params.runtimeName
81
+ * @param {string} params.runtimeVersion
82
+ * @param {object} [params.custom]
83
+ * @param {string} [params.testLevel]
84
+ * @param {string | null} params.cacheKey
85
+ * @param {Function} done
86
+ */
87
+ function fetchFromApi ({
88
+ url,
89
+ isEvpProxy,
90
+ evpProxyPrefix,
91
+ isGzipCompatible,
92
+ env,
93
+ service,
94
+ repositoryUrl,
95
+ sha,
96
+ osVersion,
97
+ osPlatform,
98
+ osArchitecture,
99
+ runtimeName,
100
+ runtimeVersion,
101
+ custom,
102
+ testLevel,
103
+ cacheKey,
33
104
  }, done) {
34
105
  const options = {
35
106
  path: '/api/v2/ci/tests/skippable',
@@ -109,7 +180,11 @@ function getSkippableSuites ({
109
180
  )
110
181
  distributionMetric(TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_BYTES, {}, res.length)
111
182
  log.debug('Number of received skippable %ss:', testLevel, skippableSuites.length)
112
- done(null, skippableSuites, correlationId)
183
+
184
+ const result = { skippableSuites, correlationId }
185
+ writeToCache(cacheKey, result)
186
+
187
+ done(null, result)
113
188
  } catch (err) {
114
189
  done(err)
115
190
  }