dd-trace 5.16.0 → 5.18.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 (74) hide show
  1. package/LICENSE-3rdparty.csv +1 -0
  2. package/ext/exporters.d.ts +1 -1
  3. package/index.d.ts +54 -1
  4. package/init.js +40 -1
  5. package/initialize.mjs +8 -5
  6. package/package.json +24 -20
  7. package/packages/datadog-core/src/storage/index.js +1 -10
  8. package/packages/datadog-esbuild/index.js +5 -1
  9. package/packages/datadog-instrumentations/src/aws-sdk.js +2 -1
  10. package/packages/datadog-instrumentations/src/cucumber.js +76 -34
  11. package/packages/datadog-instrumentations/src/helpers/hook.js +8 -3
  12. package/packages/datadog-instrumentations/src/helpers/hooks.js +3 -0
  13. package/packages/datadog-instrumentations/src/helpers/instrument.js +4 -3
  14. package/packages/datadog-instrumentations/src/helpers/register.js +56 -5
  15. package/packages/datadog-instrumentations/src/http/server.js +98 -0
  16. package/packages/datadog-instrumentations/src/mocha/main.js +12 -1
  17. package/packages/datadog-instrumentations/src/mocha/utils.js +58 -14
  18. package/packages/datadog-instrumentations/src/mocha/worker.js +1 -0
  19. package/packages/datadog-instrumentations/src/playwright.js +1 -1
  20. package/packages/datadog-instrumentations/src/undici.js +18 -0
  21. package/packages/datadog-instrumentations/src/vitest.js +303 -0
  22. package/packages/datadog-plugin-aws-sdk/src/base.js +8 -1
  23. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +9 -3
  24. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +6 -1
  25. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +23 -5
  26. package/packages/datadog-plugin-child_process/src/index.js +1 -1
  27. package/packages/datadog-plugin-cucumber/src/index.js +24 -1
  28. package/packages/datadog-plugin-mocha/src/index.js +25 -4
  29. package/packages/datadog-plugin-openai/src/index.js +52 -30
  30. package/packages/datadog-plugin-openai/src/token-estimator.js +20 -0
  31. package/packages/datadog-plugin-undici/src/index.js +12 -0
  32. package/packages/datadog-plugin-vitest/src/index.js +156 -0
  33. package/packages/dd-trace/src/appsec/blocking.js +4 -0
  34. package/packages/dd-trace/src/appsec/channels.js +1 -0
  35. package/packages/dd-trace/src/appsec/iast/path-line.js +2 -19
  36. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +4 -0
  37. package/packages/dd-trace/src/appsec/index.js +45 -11
  38. package/packages/dd-trace/src/appsec/rasp.js +32 -5
  39. package/packages/dd-trace/src/appsec/recommended.json +208 -3
  40. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +1 -0
  41. package/packages/dd-trace/src/appsec/remote_config/index.js +2 -0
  42. package/packages/dd-trace/src/appsec/reporter.js +64 -20
  43. package/packages/dd-trace/src/appsec/sdk/track_event.js +3 -0
  44. package/packages/dd-trace/src/appsec/stack_trace.js +90 -0
  45. package/packages/dd-trace/src/appsec/standalone.js +130 -0
  46. package/packages/dd-trace/src/appsec/telemetry.js +33 -1
  47. package/packages/dd-trace/src/appsec/waf/index.js +2 -2
  48. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +2 -2
  49. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +4 -2
  50. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +4 -2
  51. package/packages/dd-trace/src/config.js +110 -40
  52. package/packages/dd-trace/src/constants.js +3 -1
  53. package/packages/dd-trace/src/datastreams/processor.js +2 -1
  54. package/packages/dd-trace/src/exporters/agent/index.js +2 -2
  55. package/packages/dd-trace/src/format.js +22 -2
  56. package/packages/dd-trace/src/opentelemetry/span.js +33 -7
  57. package/packages/dd-trace/src/opentracing/propagation/text_map.js +12 -0
  58. package/packages/dd-trace/src/opentracing/span.js +42 -1
  59. package/packages/dd-trace/src/opentracing/tracer.js +2 -2
  60. package/packages/dd-trace/src/plugins/ci_plugin.js +7 -0
  61. package/packages/dd-trace/src/plugins/index.js +3 -0
  62. package/packages/dd-trace/src/plugins/util/test.js +5 -1
  63. package/packages/dd-trace/src/priority_sampler.js +2 -5
  64. package/packages/dd-trace/src/profiling/profiler.js +1 -1
  65. package/packages/dd-trace/src/proxy.js +3 -1
  66. package/packages/dd-trace/src/rate_limiter.js +2 -2
  67. package/packages/dd-trace/src/service-naming/schemas/v0/web.js +4 -0
  68. package/packages/dd-trace/src/service-naming/schemas/v1/web.js +4 -0
  69. package/packages/dd-trace/src/span_stats.js +4 -3
  70. package/packages/dd-trace/src/tagger.js +10 -1
  71. package/packages/dd-trace/src/telemetry/init-telemetry.js +75 -0
  72. package/packages/dd-trace/src/tracer.js +2 -2
  73. package/packages/dd-trace/src/util.js +6 -1
  74. package/packages/datadog-core/src/storage/async_hooks.js +0 -49
@@ -7,6 +7,7 @@ const Hook = require('./hook')
7
7
  const requirePackageJson = require('../../../dd-trace/src/require-package-json')
8
8
  const log = require('../../../dd-trace/src/log')
9
9
  const checkRequireCache = require('../check_require_cache')
10
+ const telemetry = require('../../../dd-trace/src/telemetry/init-telemetry')
10
11
 
11
12
  const {
12
13
  DD_TRACE_DISABLED_INSTRUMENTATIONS = '',
@@ -35,22 +36,38 @@ if (DD_TRACE_DEBUG && DD_TRACE_DEBUG.toLowerCase() !== 'false') {
35
36
  setImmediate(checkRequireCache.checkForPotentialConflicts)
36
37
  }
37
38
 
39
+ const seenCombo = new Set()
40
+
38
41
  // TODO: make this more efficient
39
42
  for (const packageName of names) {
40
43
  if (disabledInstrumentations.has(packageName)) continue
41
44
 
42
- Hook([packageName], (moduleExports, moduleName, moduleBaseDir, moduleVersion) => {
45
+ const hookOptions = {}
46
+
47
+ let hook = hooks[packageName]
48
+
49
+ if (typeof hook === 'object') {
50
+ hookOptions.internals = hook.esmFirst
51
+ hook = hook.fn
52
+ }
53
+
54
+ Hook([packageName], hookOptions, (moduleExports, moduleName, moduleBaseDir, moduleVersion) => {
43
55
  moduleName = moduleName.replace(pathSepExpr, '/')
44
56
 
45
57
  // This executes the integration file thus adding its entries to `instrumentations`
46
- hooks[packageName]()
58
+ hook()
47
59
 
48
60
  if (!instrumentations[packageName]) {
49
61
  return moduleExports
50
62
  }
51
63
 
52
- for (const { name, file, versions, hook } of instrumentations[packageName]) {
64
+ const namesAndSuccesses = {}
65
+ for (const { name, file, versions, hook, filePattern } of instrumentations[packageName]) {
66
+ let fullFilePattern = filePattern
53
67
  const fullFilename = filename(name, file)
68
+ if (fullFilePattern) {
69
+ fullFilePattern = filename(name, fullFilePattern)
70
+ }
54
71
 
55
72
  // Create a WeakMap associated with the hook function so that patches on the same moduleExport only happens once
56
73
  // for example by instrumenting both dns and node:dns double the spans would be created
@@ -58,13 +75,29 @@ for (const packageName of names) {
58
75
  if (!hook[HOOK_SYMBOL]) {
59
76
  hook[HOOK_SYMBOL] = new WeakMap()
60
77
  }
78
+ let matchesFile = false
79
+
80
+ matchesFile = moduleName === fullFilename
61
81
 
62
- if (moduleName === fullFilename) {
82
+ if (fullFilePattern) {
83
+ // Some libraries include a hash in their filenames when installed,
84
+ // so our instrumentation has to include a '.*' to match them for more than a single version.
85
+ matchesFile = matchesFile || new RegExp(fullFilePattern).test(moduleName)
86
+ }
87
+
88
+ if (matchesFile) {
63
89
  const version = moduleVersion || getVersion(moduleBaseDir)
90
+ if (!Object.hasOwnProperty(namesAndSuccesses, name)) {
91
+ namesAndSuccesses[name] = {
92
+ success: false,
93
+ version
94
+ }
95
+ }
64
96
 
65
97
  if (matchVersion(version, versions)) {
66
98
  // Check if the hook already has a set moduleExport
67
99
  if (hook[HOOK_SYMBOL].has(moduleExports)) {
100
+ namesAndSuccesses[name].success = true
68
101
  return moduleExports
69
102
  }
70
103
 
@@ -76,11 +109,29 @@ for (const packageName of names) {
76
109
  // Set the moduleExports in the hooks weakmap
77
110
  hook[HOOK_SYMBOL].set(moduleExports, name)
78
111
  } catch (e) {
79
- log.error(e)
112
+ log.info('Error during ddtrace instrumentation of application, aborting.')
113
+ log.info(e)
114
+ telemetry('error', [
115
+ `error_type:${e.constructor.name}`,
116
+ `integration:${name}`,
117
+ `integration_version:${version}`
118
+ ])
80
119
  }
120
+ namesAndSuccesses[name].success = true
81
121
  }
82
122
  }
83
123
  }
124
+ for (const name of Object.keys(namesAndSuccesses)) {
125
+ const { success, version } = namesAndSuccesses[name]
126
+ if (!success && !seenCombo.has(`${name}@${version}`)) {
127
+ telemetry('abort.integration', [
128
+ `integration:${name}`,
129
+ `integration_version:${version}`
130
+ ])
131
+ log.info(`Found incompatible integration version: ${name}@${version}`)
132
+ seenCombo.add(`${name}@${version}`)
133
+ }
134
+ }
84
135
 
85
136
  return moduleExports
86
137
  })
@@ -10,6 +10,7 @@ const startServerCh = channel('apm:http:server:request:start')
10
10
  const exitServerCh = channel('apm:http:server:request:exit')
11
11
  const errorServerCh = channel('apm:http:server:request:error')
12
12
  const finishServerCh = channel('apm:http:server:request:finish')
13
+ const startWriteHeadCh = channel('apm:http:server:response:writeHead:start')
13
14
  const finishSetHeaderCh = channel('datadog:http:server:response:set-header:finish')
14
15
 
15
16
  const requestFinishedSet = new WeakSet()
@@ -20,6 +21,9 @@ const httpsNames = ['https', 'node:https']
20
21
  addHook({ name: httpNames }, http => {
21
22
  shimmer.wrap(http.ServerResponse.prototype, 'emit', wrapResponseEmit)
22
23
  shimmer.wrap(http.Server.prototype, 'emit', wrapEmit)
24
+ shimmer.wrap(http.ServerResponse.prototype, 'writeHead', wrapWriteHead)
25
+ shimmer.wrap(http.ServerResponse.prototype, 'write', wrapWrite)
26
+ shimmer.wrap(http.ServerResponse.prototype, 'end', wrapEnd)
23
27
  return http
24
28
  })
25
29
 
@@ -86,3 +90,97 @@ function wrapSetHeader (res) {
86
90
  }
87
91
  })
88
92
  }
93
+
94
+ function wrapWriteHead (writeHead) {
95
+ return function wrappedWriteHead (statusCode, reason, obj) {
96
+ if (!startWriteHeadCh.hasSubscribers) {
97
+ return writeHead.apply(this, arguments)
98
+ }
99
+
100
+ const abortController = new AbortController()
101
+
102
+ if (typeof reason !== 'string') {
103
+ obj ??= reason
104
+ }
105
+
106
+ // support writeHead(200, ['key1', 'val1', 'key2', 'val2'])
107
+ if (Array.isArray(obj)) {
108
+ const headers = {}
109
+
110
+ for (let i = 0; i < obj.length; i += 2) {
111
+ headers[obj[i]] = obj[i + 1]
112
+ }
113
+
114
+ obj = headers
115
+ }
116
+
117
+ // this doesn't support explicit duplicate headers, but it's an edge case
118
+ const responseHeaders = Object.assign(this.getHeaders(), obj)
119
+
120
+ startWriteHeadCh.publish({
121
+ req: this.req,
122
+ res: this,
123
+ abortController,
124
+ statusCode,
125
+ responseHeaders
126
+ })
127
+
128
+ if (abortController.signal.aborted) {
129
+ return this
130
+ }
131
+
132
+ return writeHead.apply(this, arguments)
133
+ }
134
+ }
135
+
136
+ function wrapWrite (write) {
137
+ return function wrappedWrite () {
138
+ if (!startWriteHeadCh.hasSubscribers) {
139
+ return write.apply(this, arguments)
140
+ }
141
+
142
+ const abortController = new AbortController()
143
+
144
+ const responseHeaders = this.getHeaders()
145
+
146
+ startWriteHeadCh.publish({
147
+ req: this.req,
148
+ res: this,
149
+ abortController,
150
+ statusCode: this.statusCode,
151
+ responseHeaders
152
+ })
153
+
154
+ if (abortController.signal.aborted) {
155
+ return true
156
+ }
157
+
158
+ return write.apply(this, arguments)
159
+ }
160
+ }
161
+
162
+ function wrapEnd (end) {
163
+ return function wrappedEnd () {
164
+ if (!startWriteHeadCh.hasSubscribers) {
165
+ return end.apply(this, arguments)
166
+ }
167
+
168
+ const abortController = new AbortController()
169
+
170
+ const responseHeaders = this.getHeaders()
171
+
172
+ startWriteHeadCh.publish({
173
+ req: this.req,
174
+ res: this,
175
+ abortController,
176
+ statusCode: this.statusCode,
177
+ responseHeaders
178
+ })
179
+
180
+ if (abortController.signal.aborted) {
181
+ return this
182
+ }
183
+
184
+ return end.apply(this, arguments)
185
+ }
186
+ }
@@ -21,6 +21,7 @@ const {
21
21
  runnableWrapper,
22
22
  getOnTestHandler,
23
23
  getOnTestEndHandler,
24
+ getOnTestRetryHandler,
24
25
  getOnHookEndHandler,
25
26
  getOnFailHandler,
26
27
  getOnPendingHandler,
@@ -37,10 +38,12 @@ let isSuitesSkipped = false
37
38
  let skippedSuites = []
38
39
  let isEarlyFlakeDetectionEnabled = false
39
40
  let isSuitesSkippingEnabled = false
41
+ let isFlakyTestRetriesEnabled = false
40
42
  let earlyFlakeDetectionNumRetries = 0
41
43
  let knownTests = []
42
44
  let itrCorrelationId = ''
43
45
  let isForcedToRun = false
46
+ const config = {}
44
47
 
45
48
  // We'll preserve the original coverage here
46
49
  const originalCoverageMap = createCoverageMap()
@@ -227,6 +230,12 @@ addHook({
227
230
  isEarlyFlakeDetectionEnabled = libraryConfig.isEarlyFlakeDetectionEnabled
228
231
  isSuitesSkippingEnabled = libraryConfig.isSuitesSkippingEnabled
229
232
  earlyFlakeDetectionNumRetries = libraryConfig.earlyFlakeDetectionNumRetries
233
+ isFlakyTestRetriesEnabled = libraryConfig.isFlakyTestRetriesEnabled
234
+
235
+ config.isEarlyFlakeDetectionEnabled = isEarlyFlakeDetectionEnabled
236
+ config.isSuitesSkippingEnabled = isSuitesSkippingEnabled
237
+ config.earlyFlakeDetectionNumRetries = earlyFlakeDetectionNumRetries
238
+ config.isFlakyTestRetriesEnabled = isFlakyTestRetriesEnabled
230
239
 
231
240
  if (isEarlyFlakeDetectionEnabled) {
232
241
  knownTestsCh.publish({
@@ -317,6 +326,8 @@ addHook({
317
326
 
318
327
  this.on('test end', getOnTestEndHandler())
319
328
 
329
+ this.on('retry', getOnTestRetryHandler())
330
+
320
331
  // If the hook passes, 'hook end' will be emitted. Otherwise, 'fail' will be emitted
321
332
  this.on('hook end', getOnHookEndHandler())
322
333
 
@@ -401,7 +412,7 @@ addHook({
401
412
  name: 'mocha',
402
413
  versions: ['>=5.2.0'],
403
414
  file: 'lib/runnable.js'
404
- }, runnableWrapper)
415
+ }, (runnablePackage) => runnableWrapper(runnablePackage, config))
405
416
 
406
417
  // Only used in parallel mode (--parallel flag is passed)
407
418
  // Used to generate suite events and receive test payloads from workers
@@ -3,7 +3,8 @@
3
3
  const {
4
4
  getTestSuitePath,
5
5
  removeEfdStringFromTestName,
6
- addEfdStringToTestName
6
+ addEfdStringToTestName,
7
+ NUM_FAILED_TEST_RETRIES
7
8
  } = require('../../../dd-trace/src/plugins/util/test')
8
9
  const { channel, AsyncResource } = require('../helpers/instrument')
9
10
  const shimmer = require('../../../datadog-shimmer')
@@ -11,6 +12,8 @@ const shimmer = require('../../../datadog-shimmer')
11
12
  // test channels
12
13
  const testStartCh = channel('ci:mocha:test:start')
13
14
  const testFinishCh = channel('ci:mocha:test:finish')
15
+ // after a test has failed, we'll publish to this channel
16
+ const testRetryCh = channel('ci:mocha:test:retry')
14
17
  const errorCh = channel('ci:mocha:test:error')
15
18
  const skipCh = channel('ci:mocha:test:skip')
16
19
 
@@ -70,6 +73,10 @@ function isMochaRetry (test) {
70
73
  return test._currentRetry !== undefined && test._currentRetry !== 0
71
74
  }
72
75
 
76
+ function isLastRetry (test) {
77
+ return test._currentRetry === test._retries
78
+ }
79
+
73
80
  function getTestFullName (test) {
74
81
  return `mocha.${getTestSuitePath(test.file, process.cwd())}.${removeEfdStringFromTestName(test.fullTitle())}`
75
82
  }
@@ -84,22 +91,34 @@ function getTestStatus (test) {
84
91
  return 'pass'
85
92
  }
86
93
 
87
- function getTestAsyncResource (test) {
94
+ function getTestToArKey (test) {
88
95
  if (!test.fn) {
89
- return testToAr.get(test)
96
+ return test
90
97
  }
91
98
  if (!wrappedFunctions.has(test.fn)) {
92
- return testToAr.get(test.fn)
99
+ return test.fn
93
100
  }
94
101
  const originalFn = originalFns.get(test.fn)
95
- return testToAr.get(originalFn)
102
+ return originalFn
103
+ }
104
+
105
+ function getTestAsyncResource (test) {
106
+ const key = getTestToArKey(test)
107
+ return testToAr.get(key)
96
108
  }
97
109
 
98
- function runnableWrapper (RunnablePackage) {
110
+ function runnableWrapper (RunnablePackage, libraryConfig) {
99
111
  shimmer.wrap(RunnablePackage.prototype, 'run', run => function () {
100
112
  if (!testStartCh.hasSubscribers) {
101
113
  return run.apply(this, arguments)
102
114
  }
115
+ // Flaky test retries does not work in parallel mode
116
+ if (libraryConfig?.isFlakyTestRetriesEnabled) {
117
+ this.retries(NUM_FAILED_TEST_RETRIES)
118
+ }
119
+ // The reason why the wrapping logic is here is because we need to cover
120
+ // `afterEach` and `beforeEach` hooks as well.
121
+ // It can't be done in `getOnTestHandler` because it's only called for tests.
103
122
  const isBeforeEach = this.parent._beforeEach.includes(this)
104
123
  const isAfterEach = this.parent._afterEach.includes(this)
105
124
 
@@ -135,11 +154,16 @@ function runnableWrapper (RunnablePackage) {
135
154
 
136
155
  function getOnTestHandler (isMain, newTests) {
137
156
  return function (test) {
138
- if (isMochaRetry(test)) {
139
- return
140
- }
141
157
  const testStartLine = testToStartLine.get(test)
142
158
  const asyncResource = new AsyncResource('bound-anonymous-fn')
159
+
160
+ // This may be a retry. If this is the case, `test.fn` is already wrapped,
161
+ // so we need to restore it.
162
+ if (wrappedFunctions.has(test.fn)) {
163
+ const originalFn = originalFns.get(test.fn)
164
+ test.fn = originalFn
165
+ wrappedFunctions.delete(test.fn)
166
+ }
143
167
  testToAr.set(test.fn, asyncResource)
144
168
 
145
169
  const {
@@ -186,7 +210,7 @@ function getOnTestEndHandler () {
186
210
  // if there are afterEach to be run, we don't finish the test yet
187
211
  if (asyncResource && !test.parent._afterEach.length) {
188
212
  asyncResource.runInAsyncScope(() => {
189
- testFinishCh.publish(status)
213
+ testFinishCh.publish({ status, hasBeenRetried: isMochaRetry(test) })
190
214
  })
191
215
  }
192
216
  }
@@ -197,12 +221,17 @@ function getOnHookEndHandler () {
197
221
  const test = hook.ctx.currentTest
198
222
  if (test && hook.parent._afterEach.includes(hook)) { // only if it's an afterEach
199
223
  const isLastAfterEach = hook.parent._afterEach.indexOf(hook) === hook.parent._afterEach.length - 1
224
+ if (test._retries > 0 && !isLastRetry(test)) {
225
+ return
226
+ }
200
227
  if (isLastAfterEach) {
201
228
  const status = getTestStatus(test)
202
229
  const asyncResource = getTestAsyncResource(test)
203
- asyncResource.runInAsyncScope(() => {
204
- testFinishCh.publish(status)
205
- })
230
+ if (asyncResource) {
231
+ asyncResource.runInAsyncScope(() => {
232
+ testFinishCh.publish({ status, hasBeenRetried: isMochaRetry(test) })
233
+ })
234
+ }
206
235
  }
207
236
  }
208
237
  }
@@ -226,7 +255,7 @@ function getOnFailHandler (isMain) {
226
255
  err.message = `${testOrHook.fullTitle()}: ${err.message}`
227
256
  errorCh.publish(err)
228
257
  // if it's a hook and it has failed, 'test end' will not be called
229
- testFinishCh.publish('fail')
258
+ testFinishCh.publish({ status: 'fail', hasBeenRetried: isMochaRetry(test) })
230
259
  } else {
231
260
  errorCh.publish(err)
232
261
  }
@@ -250,6 +279,20 @@ function getOnFailHandler (isMain) {
250
279
  }
251
280
  }
252
281
 
282
+ function getOnTestRetryHandler () {
283
+ return function (test) {
284
+ const asyncResource = getTestAsyncResource(test)
285
+ if (asyncResource) {
286
+ const isFirstAttempt = test._currentRetry === 0
287
+ asyncResource.runInAsyncScope(() => {
288
+ testRetryCh.publish(isFirstAttempt)
289
+ })
290
+ }
291
+ const key = getTestToArKey(test)
292
+ testToAr.delete(key)
293
+ }
294
+ }
295
+
253
296
  function getOnPendingHandler () {
254
297
  return function (test) {
255
298
  const testStartLine = testToStartLine.get(test)
@@ -299,6 +342,7 @@ module.exports = {
299
342
  testToStartLine,
300
343
  getOnTestHandler,
301
344
  getOnTestEndHandler,
345
+ getOnTestRetryHandler,
302
346
  getOnHookEndHandler,
303
347
  getOnFailHandler,
304
348
  getOnPendingHandler,
@@ -49,3 +49,4 @@ addHook({
49
49
  versions: ['>=5.2.0'],
50
50
  file: 'lib/runnable.js'
51
51
  }, runnableWrapper)
52
+ // TODO: parallel mode does not support flaky test retries, so no library config is passed.
@@ -249,7 +249,7 @@ function testEndHandler (test, annotations, testStatus, error, isTimeout) {
249
249
  testAsyncResource.runInAsyncScope(() => {
250
250
  testFinishCh.publish({
251
251
  testStatus,
252
- steps: testResult.steps,
252
+ steps: testResult?.steps || [],
253
253
  error,
254
254
  extraTags: annotationTags,
255
255
  isNew: test._ddIsNew,
@@ -0,0 +1,18 @@
1
+ 'use strict'
2
+
3
+ const {
4
+ addHook
5
+ } = require('./helpers/instrument')
6
+ const shimmer = require('../../datadog-shimmer')
7
+
8
+ const tracingChannel = require('dc-polyfill').tracingChannel
9
+ const ch = tracingChannel('apm:undici:fetch')
10
+
11
+ const { createWrapFetch } = require('./helpers/fetch')
12
+
13
+ addHook({
14
+ name: 'undici',
15
+ versions: ['^4.4.1', '5', '>=6.0.0']
16
+ }, undici => {
17
+ return shimmer.wrap(undici, 'fetch', createWrapFetch(undici.Request, ch))
18
+ })