dd-trace 5.67.0 → 5.69.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 (132) hide show
  1. package/LICENSE-3rdparty.csv +6 -4
  2. package/README.md +0 -2
  3. package/ci/init.js +52 -54
  4. package/ext/exporters.d.ts +2 -1
  5. package/ext/exporters.js +2 -1
  6. package/index.d.ts +240 -3
  7. package/initialize.mjs +1 -1
  8. package/package.json +17 -11
  9. package/packages/datadog-core/src/storage.js +14 -13
  10. package/packages/datadog-esbuild/index.js +118 -26
  11. package/packages/datadog-instrumentations/src/aws-sdk.js +42 -4
  12. package/packages/datadog-instrumentations/src/azure-functions.js +1 -1
  13. package/packages/datadog-instrumentations/src/azure-service-bus.js +1 -1
  14. package/packages/datadog-instrumentations/src/cassandra-driver.js +2 -2
  15. package/packages/datadog-instrumentations/src/connect.js +6 -2
  16. package/packages/datadog-instrumentations/src/cucumber.js +31 -6
  17. package/packages/datadog-instrumentations/src/express.js +5 -6
  18. package/packages/datadog-instrumentations/src/fastify.js +3 -3
  19. package/packages/datadog-instrumentations/src/helpers/hook.js +28 -15
  20. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
  21. package/packages/datadog-instrumentations/src/helpers/instrument.js +15 -5
  22. package/packages/datadog-instrumentations/src/helpers/register.js +10 -3
  23. package/packages/datadog-instrumentations/src/http2/client.js +1 -0
  24. package/packages/datadog-instrumentations/src/http2/server.js +0 -1
  25. package/packages/datadog-instrumentations/src/ioredis.js +12 -1
  26. package/packages/datadog-instrumentations/src/jest.js +48 -36
  27. package/packages/datadog-instrumentations/src/limitd-client.js +2 -1
  28. package/packages/datadog-instrumentations/src/mocha/main.js +15 -7
  29. package/packages/datadog-instrumentations/src/mocha/utils.js +3 -0
  30. package/packages/datadog-instrumentations/src/mongoose.js +2 -1
  31. package/packages/datadog-instrumentations/src/oracledb.js +19 -13
  32. package/packages/datadog-instrumentations/src/pg.js +9 -5
  33. package/packages/datadog-instrumentations/src/pino.js +18 -6
  34. package/packages/datadog-instrumentations/src/playwright.js +15 -1
  35. package/packages/datadog-instrumentations/src/sequelize.js +1 -1
  36. package/packages/datadog-instrumentations/src/vitest.js +155 -62
  37. package/packages/datadog-plugin-ai/src/tracing.js +3 -3
  38. package/packages/datadog-plugin-aws-sdk/src/base.js +23 -8
  39. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/tracing.js +2 -2
  40. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +101 -2
  41. package/packages/datadog-plugin-aws-sdk/src/util.js +1 -1
  42. package/packages/datadog-plugin-confluentinc-kafka-javascript/src/index.js +6 -0
  43. package/packages/datadog-plugin-cucumber/src/index.js +4 -56
  44. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +6 -3
  45. package/packages/datadog-plugin-cypress/src/support.js +4 -0
  46. package/packages/datadog-plugin-express/src/code_origin.js +2 -2
  47. package/packages/datadog-plugin-fastify/src/code_origin.js +1 -2
  48. package/packages/datadog-plugin-jest/src/index.js +0 -21
  49. package/packages/datadog-plugin-mocha/src/index.js +3 -57
  50. package/packages/datadog-plugin-mongodb-core/src/index.js +38 -12
  51. package/packages/datadog-plugin-playwright/src/index.js +11 -5
  52. package/packages/datadog-plugin-vitest/src/index.js +5 -1
  53. package/packages/datadog-plugin-ws/src/close.js +1 -1
  54. package/packages/datadog-plugin-ws/src/producer.js +6 -1
  55. package/packages/datadog-plugin-ws/src/receiver.js +6 -1
  56. package/packages/dd-trace/src/aiguard/client.js +25 -0
  57. package/packages/dd-trace/src/aiguard/noop.js +9 -0
  58. package/packages/dd-trace/src/aiguard/sdk.js +173 -0
  59. package/packages/dd-trace/src/aiguard/tags.js +11 -0
  60. package/packages/dd-trace/src/appsec/iast/path-line.js +21 -4
  61. package/packages/dd-trace/src/appsec/iast/security-controls/parser.js +1 -1
  62. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +6 -3
  63. package/packages/dd-trace/src/appsec/stack_trace.js +20 -1
  64. package/packages/dd-trace/src/appsec/telemetry/waf.js +2 -2
  65. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +4 -4
  66. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +11 -3
  67. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +10 -1
  68. package/packages/dd-trace/src/config-helper.js +8 -1
  69. package/packages/dd-trace/src/config.js +92 -304
  70. package/packages/dd-trace/src/config_defaults.js +191 -0
  71. package/packages/dd-trace/src/crashtracking/crashtracker.js +2 -1
  72. package/packages/dd-trace/src/datastreams/fnv.js +2 -2
  73. package/packages/dd-trace/src/datastreams/index.js +23 -1
  74. package/packages/dd-trace/src/datastreams/writer.js +3 -2
  75. package/packages/dd-trace/src/debugger/devtools_client/config.js +2 -1
  76. package/packages/dd-trace/src/dogstatsd.js +4 -3
  77. package/packages/dd-trace/src/encode/0.4.js +1 -5
  78. package/packages/dd-trace/src/exporter.js +1 -0
  79. package/packages/dd-trace/src/exporters/agent/index.js +3 -2
  80. package/packages/dd-trace/src/exporters/agent/writer.js +1 -1
  81. package/packages/dd-trace/src/exporters/common/agent-info-exporter.js +3 -2
  82. package/packages/dd-trace/src/exporters/common/request.js +2 -1
  83. package/packages/dd-trace/src/exporters/span-stats/index.js +3 -2
  84. package/packages/dd-trace/src/llmobs/constants/tags.js +2 -0
  85. package/packages/dd-trace/src/llmobs/plugins/ai/index.js +15 -4
  86. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +20 -7
  87. package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +40 -13
  88. package/packages/dd-trace/src/llmobs/plugins/openai.js +7 -1
  89. package/packages/dd-trace/src/llmobs/tagger.js +8 -0
  90. package/packages/dd-trace/src/llmobs/telemetry.js +2 -1
  91. package/packages/dd-trace/src/log/index.js +27 -16
  92. package/packages/dd-trace/src/log/log.js +29 -5
  93. package/packages/dd-trace/src/log/writer.js +5 -5
  94. package/packages/dd-trace/src/noop/proxy.js +4 -0
  95. package/packages/dd-trace/src/noop/span.js +1 -0
  96. package/packages/dd-trace/src/opentelemetry/span.js +14 -3
  97. package/packages/dd-trace/src/opentracing/span.js +19 -5
  98. package/packages/dd-trace/src/payload-tagging/config/index.js +16 -0
  99. package/packages/dd-trace/src/payload-tagging/index.js +26 -15
  100. package/packages/dd-trace/src/payload-tagging/tagging.js +17 -8
  101. package/packages/dd-trace/src/pkg.js +3 -1
  102. package/packages/dd-trace/src/plugin_manager.js +20 -2
  103. package/packages/dd-trace/src/plugins/ci_plugin.js +97 -3
  104. package/packages/dd-trace/src/plugins/composite.js +3 -0
  105. package/packages/dd-trace/src/plugins/index.js +2 -0
  106. package/packages/dd-trace/src/plugins/plugin.js +67 -0
  107. package/packages/dd-trace/src/plugins/util/git-cache.js +129 -0
  108. package/packages/dd-trace/src/plugins/util/git.js +41 -27
  109. package/packages/dd-trace/src/plugins/util/test.js +56 -27
  110. package/packages/dd-trace/src/plugins/util/web.js +1 -1
  111. package/packages/dd-trace/src/priority_sampler.js +70 -46
  112. package/packages/dd-trace/src/profiler.js +4 -1
  113. package/packages/dd-trace/src/profiling/config.js +73 -42
  114. package/packages/dd-trace/src/profiling/profiler.js +3 -1
  115. package/packages/dd-trace/src/profiling/profilers/events.js +3 -8
  116. package/packages/dd-trace/src/profiling/profilers/space.js +1 -0
  117. package/packages/dd-trace/src/profiling/profilers/wall.js +196 -117
  118. package/packages/dd-trace/src/proxy.js +15 -0
  119. package/packages/dd-trace/src/rate_limiter.js +26 -1
  120. package/packages/dd-trace/src/remote_config/capabilities.js +5 -0
  121. package/packages/dd-trace/src/remote_config/manager.js +3 -2
  122. package/packages/dd-trace/src/sampling_rule.js +124 -2
  123. package/packages/dd-trace/src/span_sampler.js +19 -0
  124. package/packages/dd-trace/src/standalone/product.js +9 -0
  125. package/packages/dd-trace/src/standalone/tracesource.js +16 -1
  126. package/packages/dd-trace/src/standalone/tracesource_priority_sampler.js +13 -0
  127. package/packages/dd-trace/src/startup-log.js +21 -2
  128. package/packages/dd-trace/src/supported-configurations.json +9 -0
  129. package/packages/dd-trace/src/telemetry/logs/index.js +2 -2
  130. package/packages/dd-trace/src/util.js +1 -1
  131. package/register.js +1 -1
  132. package/version.js +4 -2
@@ -10,12 +10,13 @@ const { AsyncLocalStorage } = require('async_hooks')
10
10
  * a "handle" object, which is used as a key in a WeakMap, where the values
11
11
  * are the real store objects.
12
12
  *
13
- * @template T
13
+ * @typedef {Record<string, unknown>} Store
14
14
  */
15
15
  class DatadogStorage extends AsyncLocalStorage {
16
16
  /**
17
17
  *
18
- * @param store {DatadogStorage}
18
+ * @param store {Store}
19
+ * @override
19
20
  */
20
21
  enterWith (store) {
21
22
  const handle = {}
@@ -34,7 +35,7 @@ class DatadogStorage extends AsyncLocalStorage {
34
35
  *
35
36
  * TODO: Refactor the Scope class to use a span-only store and remove this.
36
37
  *
37
- * @returns {{}}
38
+ * @returns {Store}
38
39
  */
39
40
  getHandle () {
40
41
  return super.getStore()
@@ -46,9 +47,9 @@ class DatadogStorage extends AsyncLocalStorage {
46
47
  * retrieved through `getHandle()` can also be passed in to be used as the
47
48
  * key. This is useful if you've stashed a handle somewhere and want to
48
49
  * retrieve the store with it.
49
- *
50
- * @param [handle] {{}}
51
- * @returns {T | undefined}
50
+ * @param {{}} [handle]
51
+ * @returns {Store | undefined}
52
+ * @override
52
53
  */
53
54
  getStore (handle) {
54
55
  if (!handle) {
@@ -66,11 +67,12 @@ class DatadogStorage extends AsyncLocalStorage {
66
67
  * when dealing with the parent store, so that we don't have to access the
67
68
  * WeakMap.
68
69
  * @template R
69
- * @template TArgs extends any[]
70
- * @param store {DatadogStorage}
71
- * @param fn {() => R}
72
- * @param args {TArgs}
73
- * @returns {void}
70
+ * @template TArgs = unknown[]
71
+ * @param {Store} store
72
+ * @param {() => R} fn
73
+ * @param {...TArgs} args
74
+ * @returns {R}
75
+ * @override
74
76
  */
75
77
  run (store, fn, ...args) {
76
78
  const prior = super.getStore()
@@ -85,8 +87,7 @@ class DatadogStorage extends AsyncLocalStorage {
85
87
 
86
88
  /**
87
89
  * This is the map from handles to real stores, used in the class above.
88
- * @template T
89
- * @type {WeakMap<WeakKey, T>}
90
+ * @type {WeakMap<WeakKey, Store>}
90
91
  */
91
92
  const stores = new WeakMap()
92
93
 
@@ -8,6 +8,8 @@ const extractPackageAndModulePath = require(
8
8
  '../datadog-instrumentations/src/helpers/extract-package-and-module-path.js'
9
9
  )
10
10
 
11
+ let rewriter
12
+
11
13
  for (const hook of Object.values(hooks)) {
12
14
  if (typeof hook === 'object') {
13
15
  hook.fn()
@@ -33,6 +35,7 @@ const RAW_BUILTINS = require('module').builtinModules
33
35
  const CHANNEL = 'dd-trace:bundler:load'
34
36
  const path = require('path')
35
37
  const fs = require('fs')
38
+ const { execSync } = require('child_process')
36
39
 
37
40
  const builtins = new Set()
38
41
 
@@ -42,6 +45,7 @@ for (const builtin of RAW_BUILTINS) {
42
45
  }
43
46
 
44
47
  const DEBUG = !!process.env.DD_TRACE_DEBUG
48
+ const DD_IAST_ENABLED = process.env.DD_IAST_ENABLED?.toLowerCase() === 'true' || process.env.DD_IAST_ENABLED === '1'
45
49
 
46
50
  // We don't want to handle any built-in packages
47
51
  // Those packages will still be handled via RITM
@@ -61,11 +65,57 @@ function isESMBuild (build) {
61
65
  return format === 'esm' || outputFile?.endsWith('.mjs') || outExtension === '.mjs'
62
66
  }
63
67
 
68
+ function getGitMetadata () {
69
+ const gitMetadata = {
70
+ repositoryURL: null,
71
+ commitSHA: null
72
+ }
73
+
74
+ try {
75
+ gitMetadata.repositoryURL = execSync('git config --get remote.origin.url', {
76
+ encoding: 'utf8',
77
+ stdio: ['pipe', 'pipe', 'ignore'],
78
+ cwd: process.cwd()
79
+ }).trim()
80
+ } catch (e) {
81
+ if (DEBUG) {
82
+ console.warn('Warning: failed to get git repository URL:', e.message)
83
+ }
84
+ }
85
+
86
+ try {
87
+ gitMetadata.commitSHA = execSync('git rev-parse HEAD', {
88
+ encoding: 'utf8',
89
+ stdio: ['pipe', 'pipe', 'ignore'],
90
+ cwd: process.cwd()
91
+ }).trim()
92
+ } catch (e) {
93
+ if (DEBUG) {
94
+ console.warn('Warning: failed to get git commit SHA:', e.message)
95
+ }
96
+ }
97
+
98
+ return gitMetadata
99
+ }
100
+
64
101
  module.exports.setup = function (build) {
102
+ if (DD_IAST_ENABLED) {
103
+ const iastRewriter = require('../dd-trace/src/appsec/iast/taint-tracking/rewriter')
104
+ rewriter = iastRewriter.getRewriter()
105
+ }
106
+
107
+ const isSourceMapEnabled = !!build.initialOptions.sourcemap ||
108
+ ['internal', 'both'].includes(build.initialOptions.sourcemap)
65
109
  const externalModules = new Set(build.initialOptions.external || [])
110
+ build.initialOptions.banner ??= {}
111
+ build.initialOptions.banner.js ??= ''
112
+ if (DD_IAST_ENABLED) {
113
+ build.initialOptions.banner.js =
114
+ `globalThis.__DD_ESBUILD_IAST_${isSourceMapEnabled ? 'WITH_SM' : 'WITH_NO_SM'} = true;
115
+ ${isSourceMapEnabled ? `globalThis.__DD_ESBUILD_BASEPATH = '${require('../dd-trace/src/util').ddBasePath}';` : ''}
116
+ ${build.initialOptions.banner.js}`
117
+ }
66
118
  if (isESMBuild(build)) {
67
- build.initialOptions.banner ??= {}
68
- build.initialOptions.banner.js ??= ''
69
119
  if (!build.initialOptions.banner.js.includes('import { createRequire as $dd_createRequire } from \'module\'')) {
70
120
  build.initialOptions.banner.js = `import { createRequire as $dd_createRequire } from 'module';
71
121
  import { fileURLToPath as $dd_fileURLToPath } from 'url';
@@ -77,6 +127,28 @@ ${build.initialOptions.banner.js}`
77
127
  }
78
128
  }
79
129
 
130
+ // Get git metadata at build time and add it to the banner for both ESM and CommonJS builds
131
+ const gitMetadata = getGitMetadata()
132
+ if (gitMetadata.repositoryURL || gitMetadata.commitSHA) {
133
+ build.initialOptions.banner ??= {}
134
+ build.initialOptions.banner.js ??= ''
135
+
136
+ build.initialOptions.banner.js = `if (typeof process === 'object' && process !== null &&
137
+ process.env !== null && typeof process.env === 'object') {
138
+ ${gitMetadata.repositoryURL ? `process.env.DD_GIT_REPOSITORY_URL = '${gitMetadata.repositoryURL}';` : ''}
139
+ ${gitMetadata.commitSHA ? `process.env.DD_GIT_COMMIT_SHA = '${gitMetadata.commitSHA}';` : ''}
140
+ }
141
+ ${build.initialOptions.banner.js}`
142
+
143
+ if (DEBUG) {
144
+ console.log('Info: automatically injected git metadata:')
145
+ console.log(`DD_GIT_REPOSITORY_URL: ${gitMetadata.repositoryURL || 'not available'}`)
146
+ console.log(`DD_GIT_COMMIT_SHA: ${gitMetadata.commitSHA || 'not available'}`)
147
+ }
148
+ } else if (DEBUG) {
149
+ console.warn('Warning: No git metadata available - skipping injection')
150
+ }
151
+
80
152
  build.onResolve({ filter: /.*/ }, args => {
81
153
  if (externalModules.has(args.path)) {
82
154
  // Internal Node.js packages will still be instrumented via require()
@@ -84,13 +156,6 @@ ${build.initialOptions.banner.js}`
84
156
  return
85
157
  }
86
158
 
87
- // TODO: Should this also check for namespace === 'file'?
88
- if (args.path.startsWith('.') && !args.importer.includes('node_modules/')) {
89
- // This is local application code, not an instrumented package
90
- if (DEBUG) console.log(`LOCAL: ${args.path}`)
91
- return
92
- }
93
-
94
159
  // TODO: Should this also check for namespace === 'file'?
95
160
  if (!modulesOfInterest.has(args.path) &&
96
161
  args.path.startsWith('@') &&
@@ -110,6 +175,21 @@ ${build.initialOptions.banner.js}`
110
175
  }
111
176
  return
112
177
  }
178
+
179
+ if (args.path.startsWith('.') && !args.importer.includes('node_modules/')) {
180
+ // It is local application code, not an instrumented package
181
+ if (DEBUG) console.log(`APP: ${args.path}`)
182
+
183
+ return {
184
+ path: fullPathToModule,
185
+ pluginData: {
186
+ path: args.path,
187
+ full: fullPathToModule,
188
+ applicationFile: true
189
+ }
190
+ }
191
+ }
192
+
113
193
  const extracted = extractPackageAndModulePath(fullPathToModule)
114
194
 
115
195
  const internal = builtins.has(args.path)
@@ -159,22 +239,19 @@ ${build.initialOptions.banner.js}`
159
239
  })
160
240
 
161
241
  build.onLoad({ filter: /.*/ }, args => {
162
- if (!args.pluginData?.pkgOfInterest) {
163
- return
164
- }
165
-
166
- const data = args.pluginData
242
+ if (args.pluginData?.pkgOfInterest) {
243
+ const data = args.pluginData
167
244
 
168
- if (DEBUG) console.log(`LOAD: ${data.pkg}@${data.version}, pkg "${data.path}"`)
245
+ if (DEBUG) console.log(`LOAD: ${data.pkg}@${data.version}, pkg "${data.path}"`)
169
246
 
170
- const pkgPath = data.raw !== data.pkg
171
- ? `${data.pkg}/${data.path}`
172
- : data.pkg
247
+ const pkgPath = data.raw !== data.pkg
248
+ ? `${data.pkg}/${data.path}`
249
+ : data.pkg
173
250
 
174
- // Read the content of the module file of interest
175
- const fileCode = fs.readFileSync(args.path, 'utf8')
251
+ // Read the content of the module file of interest
252
+ const fileCode = fs.readFileSync(args.path, 'utf8')
176
253
 
177
- const contents = `
254
+ const contents = `
178
255
  (function() {
179
256
  ${fileCode}
180
257
  })(...arguments);
@@ -192,12 +269,27 @@ ${build.initialOptions.banner.js}`
192
269
  module.exports = payload.module;
193
270
  }
194
271
  `
272
+ // https://esbuild.github.io/plugins/#on-load-results
273
+ return {
274
+ contents,
275
+ loader: 'js',
276
+ resolveDir: path.dirname(args.path)
277
+ }
278
+ }
195
279
 
196
- // https://esbuild.github.io/plugins/#on-load-results
197
- return {
198
- contents,
199
- loader: 'js',
200
- resolveDir: path.dirname(args.path)
280
+ if (DD_IAST_ENABLED && args.pluginData?.applicationFile) {
281
+ const ext = path.extname(args.path).toLowerCase()
282
+ const isJs = /^\.(js|mjs|cjs)$/.test(ext)
283
+ if (!isJs) return
284
+
285
+ if (DEBUG) console.log(`REWRITE: ${args.path}`)
286
+ const fileCode = fs.readFileSync(args.path, 'utf8')
287
+ const rewritten = rewriter.rewrite(fileCode, args.path, ['iast'])
288
+ return {
289
+ contents: rewritten.content,
290
+ loader: 'js',
291
+ resolveDir: path.dirname(args.path)
292
+ }
201
293
  }
202
294
  })
203
295
  }
@@ -61,7 +61,6 @@ function wrapSmithySend (send) {
61
61
 
62
62
  const startCh = channel(`apm:aws:request:start:${channelSuffix}`)
63
63
  const regionCh = channel(`apm:aws:request:region:${channelSuffix}`)
64
- const completeChannel = channel(`apm:aws:request:complete:${channelSuffix}`)
65
64
  const responseStartChannel = channel(`apm:aws:response:start:${channelSuffix}`)
66
65
  const responseFinishChannel = channel(`apm:aws:response:finish:${channelSuffix}`)
67
66
 
@@ -87,7 +86,7 @@ function wrapSmithySend (send) {
87
86
  args[args.length - 1] = shimmer.wrapFunction(cb, cb => function (err, result) {
88
87
  addResponse(ctx, err, result)
89
88
 
90
- completeChannel.publish(ctx)
89
+ handleCompletion(result, ctx, channelSuffix)
91
90
 
92
91
  const responseCtx = { request, response: ctx.response }
93
92
 
@@ -102,12 +101,12 @@ function wrapSmithySend (send) {
102
101
  .then(
103
102
  result => {
104
103
  addResponse(ctx, null, result)
105
- completeChannel.publish(ctx)
104
+ handleCompletion(result, ctx, channelSuffix)
106
105
  return result
107
106
  },
108
107
  error => {
109
108
  addResponse(ctx, error)
110
- completeChannel.publish(ctx)
109
+ handleCompletion(null, ctx, channelSuffix)
111
110
  throw error
112
111
  }
113
112
  )
@@ -118,6 +117,45 @@ function wrapSmithySend (send) {
118
117
  }
119
118
  }
120
119
 
120
+ function handleCompletion (result, ctx, channelSuffix) {
121
+ const completeChannel = channel(`apm:aws:request:complete:${channelSuffix}`)
122
+ const streamedChunkChannel = channel(`apm:aws:response:streamed-chunk:${channelSuffix}`)
123
+
124
+ const iterator = result?.body?.[Symbol.asyncIterator]
125
+ if (!iterator) {
126
+ completeChannel.publish(ctx)
127
+ return
128
+ }
129
+
130
+ shimmer.wrap(result.body, Symbol.asyncIterator, function (asyncIterator) {
131
+ return function () {
132
+ const iterator = asyncIterator.apply(this, arguments)
133
+ shimmer.wrap(iterator, 'next', function (next) {
134
+ return function () {
135
+ return next.apply(this, arguments)
136
+ .then(result => {
137
+ const { done, value: chunk } = result
138
+ streamedChunkChannel.publish({ ctx, chunk, done })
139
+
140
+ if (done) {
141
+ completeChannel.publish(ctx)
142
+ }
143
+
144
+ return result
145
+ })
146
+ .catch(err => {
147
+ addResponse(ctx, err)
148
+ completeChannel.publish(ctx)
149
+ throw err
150
+ })
151
+ }
152
+ })
153
+
154
+ return iterator
155
+ }
156
+ })
157
+ }
158
+
121
159
  function wrapCb (cb, serviceName, ctx) {
122
160
  // eslint-disable-next-line n/handle-callback-err
123
161
  return shimmer.wrapFunction(cb, cb => function wrappedCb (err, response) {
@@ -8,7 +8,7 @@ const dc = require('dc-polyfill')
8
8
 
9
9
  const azureFunctionsChannel = dc.tracingChannel('datadog:azure:functions:invoke')
10
10
 
11
- addHook({ name: '@azure/functions', versions: ['>=4'] }, azureFunction => {
11
+ addHook({ name: '@azure/functions', versions: ['>=4'], patchDefault: false }, (azureFunction) => {
12
12
  const { app } = azureFunction
13
13
 
14
14
  // Http triggers
@@ -11,7 +11,7 @@ const producerStartCh = channel('apm:azure-service-bus:send:start')
11
11
  const producerErrorCh = channel('apm:azure-service-bus:send:error')
12
12
  const producerFinishCh = channel('apm:azure-service-bus:send:finish')
13
13
 
14
- addHook({ name: '@azure/service-bus', versions: ['>=7.9.2'] }, (obj) => {
14
+ addHook({ name: '@azure/service-bus', versions: ['>=7.9.2'], patchDefault: false }, (obj) => {
15
15
  const ServiceBusClient = obj.ServiceBusClient
16
16
  shimmer.wrap(ServiceBusClient.prototype, 'createSender', createSender => function (queueOrTopicName) {
17
17
  const sender = createSender.apply(this, arguments)
@@ -45,7 +45,7 @@ addHook({ name: 'cassandra-driver', versions: ['>=3.0.0'] }, cassandra => {
45
45
  return cassandra
46
46
  })
47
47
 
48
- addHook({ name: 'cassandra-driver', versions: ['>=4.4'] }, cassandra => {
48
+ addHook({ name: 'cassandra-driver', versions: ['>=4.4'], patchDefault: false }, (cassandra) => {
49
49
  shimmer.wrap(cassandra.Client.prototype, '_execute', _execute => function (query, params, execOptions, callback) {
50
50
  if (!startCh.hasSubscribers) {
51
51
  return _execute.apply(this, arguments)
@@ -68,7 +68,7 @@ const isValid = (args) => {
68
68
  return args.length === 4 || typeof args[3] === 'function'
69
69
  }
70
70
 
71
- addHook({ name: 'cassandra-driver', versions: ['3 - 4.3'] }, cassandra => {
71
+ addHook({ name: 'cassandra-driver', versions: ['3 - 4.3'], patchDefault: false }, (cassandra) => {
72
72
  shimmer.wrap(cassandra.Client.prototype, '_innerExecute', _innerExecute =>
73
73
  function (query, params, execOptions, callback) {
74
74
  if (!startCh.hasSubscribers) {
@@ -102,11 +102,15 @@ function wrapNext (req, next) {
102
102
  })
103
103
  }
104
104
 
105
- addHook({ name: 'connect', versions: ['>=3'] }, connect => {
105
+ addHook({ name: 'connect', versions: ['>=3.4.0'] }, (connect) => {
106
106
  return shimmer.wrapFunction(connect, connect => wrapConnect(connect))
107
107
  })
108
108
 
109
- addHook({ name: 'connect', versions: ['2.2.2'] }, connect => {
109
+ addHook({ name: 'connect', versions: ['>=3 <3.4.0'], file: 'lib/connect.js' }, (connect) => {
110
+ return shimmer.wrapFunction(connect, connect => wrapConnect(connect))
111
+ })
112
+
113
+ addHook({ name: 'connect', versions: ['2.2.2'], file: 'lib/connect.js' }, connect => {
110
114
  shimmer.wrap(connect.proto, 'use', wrapUse)
111
115
  shimmer.wrap(connect.proto, 'handle', wrapHandle)
112
116
 
@@ -82,10 +82,14 @@ let testManagementAttemptToFixRetries = 0
82
82
  let testManagementTests = {}
83
83
  let modifiedTests = {}
84
84
  let numTestRetries = 0
85
- let knownTests = []
85
+ let knownTests = {}
86
86
  let skippedSuites = []
87
87
  let isSuitesSkipped = false
88
88
 
89
+ function isValidKnownTests (receivedKnownTests) {
90
+ return !!receivedKnownTests.cucumber
91
+ }
92
+
89
93
  function getSuiteStatusFromTestStatuses (testStatuses) {
90
94
  if (testStatuses.includes('fail')) {
91
95
  return 'fail'
@@ -123,7 +127,10 @@ function getStatusFromResultLatest (result) {
123
127
  }
124
128
 
125
129
  function isNewTest (testSuite, testName) {
126
- const testsForSuite = knownTests.cucumber?.[testSuite] || []
130
+ if (!isValidKnownTests(knownTests)) {
131
+ return false
132
+ }
133
+ const testsForSuite = knownTests.cucumber[testSuite] || []
127
134
  return !testsForSuite.includes(testName)
128
135
  }
129
136
 
@@ -508,9 +515,9 @@ function getWrappedStart (start, frameworkVersion, isParallel = false, isCoordin
508
515
  pickleByFile = isCoordinator ? getPickleByFileNew(this) : getPickleByFile(this)
509
516
 
510
517
  if (isKnownTestsEnabled) {
511
- const isFaulty = getIsFaultyEarlyFlakeDetection(
518
+ const isFaulty = !isValidKnownTests(knownTests) || getIsFaultyEarlyFlakeDetection(
512
519
  Object.keys(pickleByFile),
513
- knownTests.cucumber || {},
520
+ knownTests.cucumber,
514
521
  earlyFlakeDetectionFaultyThreshold
515
522
  )
516
523
  if (isFaulty) {
@@ -592,6 +599,9 @@ function getWrappedStart (start, frameworkVersion, isParallel = false, isCoordin
592
599
  // Handles EFD in both the main process and the worker process.
593
600
  function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = false, isWorker = false) {
594
601
  return async function () {
602
+ if (!testSuiteFinishCh.hasSubscribers) {
603
+ return runTestCaseFunction.apply(this, arguments)
604
+ }
595
605
  const pickle = isNewerCucumberVersion
596
606
  ? arguments[0].pickle
597
607
  : this.eventDataCollector.getPickle(arguments[0])
@@ -747,6 +757,9 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
747
757
 
748
758
  function getWrappedParseWorkerMessage (parseWorkerMessageFunction, isNewVersion) {
749
759
  return function (worker, message) {
760
+ if (!testSuiteFinishCh.hasSubscribers) {
761
+ return parseWorkerMessageFunction.apply(this, arguments)
762
+ }
750
763
  // If the message is an array, it's a dd-trace message, so we need to stop cucumber processing,
751
764
  // or cucumber will throw an error
752
765
  // TODO: identify the message better
@@ -972,10 +985,17 @@ addHook({
972
985
  )
973
986
  // EFD in parallel mode only supported in >=11.0.0
974
987
  shimmer.wrap(adapterPackage.ChildProcessAdapter.prototype, 'startWorker', startWorker => function () {
975
- if (isKnownTestsEnabled) {
988
+ if (isKnownTestsEnabled && isValidKnownTests(knownTests)) {
989
+ this.options.worldParameters._ddIsKnownTestsEnabled = true
976
990
  this.options.worldParameters._ddIsEarlyFlakeDetectionEnabled = isEarlyFlakeDetectionEnabled
977
991
  this.options.worldParameters._ddKnownTests = knownTests
978
992
  this.options.worldParameters._ddEarlyFlakeDetectionNumRetries = earlyFlakeDetectionNumRetries
993
+ } else {
994
+ isEarlyFlakeDetectionEnabled = false
995
+ isKnownTestsEnabled = false
996
+ this.options.worldParameters._ddIsEarlyFlakeDetectionEnabled = false
997
+ this.options.worldParameters._ddIsKnownTestsEnabled = false
998
+ this.options.worldParameters._ddEarlyFlakeDetectionNumRetries = 0
979
999
  }
980
1000
 
981
1001
  if (isImpactedTestsEnabled) {
@@ -1001,9 +1021,14 @@ addHook({
1001
1021
  'initialize',
1002
1022
  initialize => async function () {
1003
1023
  await initialize.apply(this, arguments)
1004
- isKnownTestsEnabled = !!this.options.worldParameters._ddKnownTests
1024
+ isKnownTestsEnabled = !!this.options.worldParameters._ddIsKnownTestsEnabled
1005
1025
  if (isKnownTestsEnabled) {
1006
1026
  knownTests = this.options.worldParameters._ddKnownTests
1027
+ // if for whatever reason the worker does not receive valid known tests, we disable EFD and known tests
1028
+ if (!isValidKnownTests(knownTests)) {
1029
+ isKnownTestsEnabled = false
1030
+ knownTests = {}
1031
+ }
1007
1032
  }
1008
1033
  isEarlyFlakeDetectionEnabled = !!this.options.worldParameters._ddIsEarlyFlakeDetectionEnabled
1009
1034
  if (isEarlyFlakeDetectionEnabled) {
@@ -2,8 +2,7 @@
2
2
 
3
3
  const { createWrapRouterMethod } = require('./router')
4
4
  const shimmer = require('../../datadog-shimmer')
5
- const { addHook, channel } = require('./helpers/instrument')
6
- const tracingChannel = require('dc-polyfill').tracingChannel
5
+ const { addHook, channel, tracingChannel } = require('./helpers/instrument')
7
6
 
8
7
  const handleChannel = channel('apm:express:request:handle')
9
8
 
@@ -57,7 +56,7 @@ function wrapResponseRender (render) {
57
56
  }
58
57
  }
59
58
 
60
- addHook({ name: 'express', versions: ['>=4'] }, express => {
59
+ addHook({ name: 'express', versions: ['>=4'], file: ['lib/express.js'] }, express => {
61
60
  shimmer.wrap(express.application, 'handle', wrapHandle)
62
61
 
63
62
  shimmer.wrap(express.response, 'json', wrapResponseJson)
@@ -70,7 +69,7 @@ addHook({ name: 'express', versions: ['>=4'] }, express => {
70
69
  // Express 5 does not rely on router in the same way as v4 and should not be instrumented anymore.
71
70
  // It would otherwise produce spans for router and express, and so duplicating them.
72
71
  // We now fall back to router instrumentation
73
- addHook({ name: 'express', versions: ['4'] }, express => {
72
+ addHook({ name: 'express', versions: ['4'], file: 'lib/express.js' }, express => {
74
73
  shimmer.wrap(express.Router, 'use', wrapRouterMethod)
75
74
  shimmer.wrap(express.Router, 'route', wrapRouterMethod)
76
75
 
@@ -132,12 +131,12 @@ function wrapProcessParamsMethod (requestPositionInArguments) {
132
131
  }
133
132
  }
134
133
 
135
- addHook({ name: 'express', versions: ['>=4.0.0 <4.3.0'] }, express => {
134
+ addHook({ name: 'express', versions: ['>=4.0.0 <4.3.0'], file: ['lib/express.js'] }, express => {
136
135
  shimmer.wrap(express.Router, 'process_params', wrapProcessParamsMethod(1))
137
136
  return express
138
137
  })
139
138
 
140
- addHook({ name: 'express', versions: ['>=4.3.0 <5.0.0'] }, express => {
139
+ addHook({ name: 'express', versions: ['>=4.3.0 <5.0.0'], file: ['lib/express.js'] }, express => {
141
140
  shimmer.wrap(express.Router, 'process_params', wrapProcessParamsMethod(2))
142
141
  return express
143
142
  })
@@ -265,7 +265,7 @@ function canPublishResponsePayload (payload) {
265
265
  !ArrayBuffer.isView(payload) // TypedArray
266
266
  }
267
267
 
268
- addHook({ name: 'fastify', versions: ['>=3'] }, fastify => {
268
+ addHook({ name: 'fastify', versions: ['>=3'] }, (fastify) => {
269
269
  const wrapped = shimmer.wrapFunction(fastify, fastify => wrapFastify(fastify, true))
270
270
 
271
271
  wrapped.fastify = wrapped
@@ -274,11 +274,11 @@ addHook({ name: 'fastify', versions: ['>=3'] }, fastify => {
274
274
  return wrapped
275
275
  })
276
276
 
277
- addHook({ name: 'fastify', versions: ['2'] }, fastify => {
277
+ addHook({ name: 'fastify', versions: ['2'] }, (fastify) => {
278
278
  return shimmer.wrapFunction(fastify, fastify => wrapFastify(fastify, true))
279
279
  })
280
280
 
281
- addHook({ name: 'fastify', versions: ['1'] }, fastify => {
281
+ addHook({ name: 'fastify', versions: ['1'] }, (fastify) => {
282
282
  return shimmer.wrapFunction(fastify, fastify => wrapFastify(fastify, false))
283
283
  })
284
284
 
@@ -1,7 +1,6 @@
1
1
  'use strict'
2
-
3
- const path = require('path')
4
2
  const iitm = require('../../../dd-trace/src/iitm')
3
+ const path = require('path')
5
4
  const ritm = require('../../../dd-trace/src/ritm')
6
5
 
7
6
  /**
@@ -20,29 +19,43 @@ function Hook (modules, hookOptions, onrequire) {
20
19
  }
21
20
 
22
21
  this._patched = Object.create(null)
22
+ const patched = new WeakMap()
23
23
 
24
- const safeHook = (moduleExports, moduleName, moduleBaseDir, moduleVersion) => {
24
+ const safeHook = (moduleExports, moduleName, moduleBaseDir, moduleVersion, isIitm) => {
25
25
  const parts = [moduleBaseDir, moduleName].filter(Boolean)
26
26
  const filename = path.join(...parts)
27
27
 
28
- if (this._patched[filename]) return moduleExports
28
+ if (this._patched[filename] && patched.has(moduleExports)) {
29
+ return patched.get(moduleExports)
30
+ }
29
31
 
30
- this._patched[filename] = true
32
+ let defaultWrapResult
33
+
34
+ if (
35
+ isIitm &&
36
+ moduleExports.default &&
37
+ (typeof moduleExports.default === 'object' ||
38
+ typeof moduleExports.default === 'function')
39
+ ) {
40
+ defaultWrapResult = onrequire(moduleExports.default, moduleName, moduleBaseDir, moduleVersion, isIitm)
41
+ }
42
+
43
+ const newExports = onrequire(moduleExports, moduleName, moduleBaseDir, moduleVersion, isIitm)
44
+
45
+ if (defaultWrapResult) newExports.default = defaultWrapResult
31
46
 
32
- return onrequire(moduleExports, moduleName, moduleBaseDir, moduleVersion)
47
+ this._patched[filename] = true
48
+ if (newExports &&
49
+ (typeof newExports === 'object' ||
50
+ typeof newExports === 'function')) {
51
+ patched.set(moduleExports, newExports)
52
+ }
53
+ return newExports
33
54
  }
34
55
 
35
56
  this._ritmHook = ritm(modules, {}, safeHook)
36
57
  this._iitmHook = iitm(modules, hookOptions, (moduleExports, moduleName, moduleBaseDir) => {
37
- // TODO: Move this logic to import-in-the-middle and only do it for CommonJS
38
- // modules and not ESM. In the meantime, all the modules we instrument are
39
- // CommonJS modules for which the default export is always moved to
40
- // `default` anyway.
41
- if (moduleExports && moduleExports.default) {
42
- moduleExports.default = safeHook(moduleExports.default, moduleName, moduleBaseDir)
43
- return moduleExports
44
- }
45
- return safeHook(moduleExports, moduleName, moduleBaseDir)
58
+ return safeHook(moduleExports, moduleName, moduleBaseDir, null, true)
46
59
  })
47
60
  }
48
61
 
@@ -16,6 +16,7 @@ module.exports = {
16
16
  '@graphql-tools/executor': () => require('../graphql'),
17
17
  '@grpc/grpc-js': () => require('../grpc'),
18
18
  '@hapi/hapi': () => require('../hapi'),
19
+ '@happy-dom/jest-environment': () => require('../jest'),
19
20
  '@jest/core': () => require('../jest'),
20
21
  '@jest/reporters': () => require('../jest'),
21
22
  '@jest/test-sequencer': () => require('../jest'),
@@ -130,6 +131,7 @@ module.exports = {
130
131
  sequelize: () => require('../sequelize'),
131
132
  sharedb: () => require('../sharedb'),
132
133
  tedious: () => require('../tedious'),
134
+ tinypool: { esmFirst: true, fn: () => require('../vitest') },
133
135
  undici: () => require('../undici'),
134
136
  url: () => require('../url'),
135
137
  vitest: { esmFirst: true, fn: () => require('../vitest') },