dd-trace 5.51.0 → 5.53.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 (98) hide show
  1. package/LICENSE-3rdparty.csv +0 -6
  2. package/README.md +5 -0
  3. package/index.d.ts +88 -6
  4. package/package.json +3 -9
  5. package/packages/datadog-instrumentations/src/amqplib.js +8 -5
  6. package/packages/datadog-instrumentations/src/child_process.js +2 -1
  7. package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +406 -0
  8. package/packages/datadog-instrumentations/src/couchbase.js +2 -1
  9. package/packages/datadog-instrumentations/src/cucumber.js +43 -45
  10. package/packages/datadog-instrumentations/src/dns.js +16 -14
  11. package/packages/datadog-instrumentations/src/express.js +2 -6
  12. package/packages/datadog-instrumentations/src/fs.js +43 -51
  13. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
  14. package/packages/datadog-instrumentations/src/helpers/register.js +17 -12
  15. package/packages/datadog-instrumentations/src/http/client.js +2 -1
  16. package/packages/datadog-instrumentations/src/iovalkey.js +51 -0
  17. package/packages/datadog-instrumentations/src/jest.js +53 -40
  18. package/packages/datadog-instrumentations/src/kafkajs.js +21 -8
  19. package/packages/datadog-instrumentations/src/mocha/main.js +33 -46
  20. package/packages/datadog-instrumentations/src/mocha/utils.js +76 -74
  21. package/packages/datadog-instrumentations/src/mysql2.js +3 -1
  22. package/packages/datadog-instrumentations/src/net.js +27 -29
  23. package/packages/datadog-instrumentations/src/next.js +6 -14
  24. package/packages/datadog-instrumentations/src/pg.js +15 -7
  25. package/packages/datadog-instrumentations/src/playwright.js +64 -67
  26. package/packages/datadog-instrumentations/src/url.js +9 -17
  27. package/packages/datadog-instrumentations/src/vitest.js +66 -72
  28. package/packages/datadog-plugin-confluentinc-kafka-javascript/src/batch-consumer.js +11 -0
  29. package/packages/datadog-plugin-confluentinc-kafka-javascript/src/consumer.js +11 -0
  30. package/packages/datadog-plugin-confluentinc-kafka-javascript/src/index.js +19 -0
  31. package/packages/datadog-plugin-confluentinc-kafka-javascript/src/producer.js +11 -0
  32. package/packages/datadog-plugin-cucumber/src/index.js +32 -18
  33. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +3 -0
  34. package/packages/datadog-plugin-dns/src/lookup.js +10 -5
  35. package/packages/datadog-plugin-dns/src/lookup_service.js +6 -2
  36. package/packages/datadog-plugin-dns/src/resolve.js +5 -2
  37. package/packages/datadog-plugin-dns/src/reverse.js +6 -2
  38. package/packages/datadog-plugin-fs/src/index.js +9 -2
  39. package/packages/datadog-plugin-iovalkey/src/index.js +18 -0
  40. package/packages/datadog-plugin-jest/src/index.js +17 -8
  41. package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +2 -1
  42. package/packages/datadog-plugin-kafkajs/src/consumer.js +12 -21
  43. package/packages/datadog-plugin-kafkajs/src/producer.js +12 -5
  44. package/packages/datadog-plugin-kafkajs/src/utils.js +27 -0
  45. package/packages/datadog-plugin-langchain/src/index.js +0 -1
  46. package/packages/datadog-plugin-mocha/src/index.js +58 -35
  47. package/packages/datadog-plugin-net/src/ipc.js +6 -4
  48. package/packages/datadog-plugin-net/src/tcp.js +15 -9
  49. package/packages/datadog-plugin-pg/src/index.js +5 -1
  50. package/packages/datadog-plugin-playwright/src/index.js +29 -20
  51. package/packages/datadog-plugin-redis/src/index.js +8 -3
  52. package/packages/datadog-plugin-vitest/src/index.js +67 -44
  53. package/packages/datadog-shimmer/src/shimmer.js +164 -33
  54. package/packages/dd-trace/src/appsec/api_security_sampler.js +20 -12
  55. package/packages/dd-trace/src/appsec/graphql.js +2 -2
  56. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +14 -9
  57. package/packages/dd-trace/src/appsec/index.js +15 -12
  58. package/packages/dd-trace/src/appsec/rasp/index.js +4 -2
  59. package/packages/dd-trace/src/appsec/rasp/utils.js +11 -6
  60. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +2 -2
  61. package/packages/dd-trace/src/appsec/telemetry/index.js +1 -2
  62. package/packages/dd-trace/src/appsec/telemetry/rasp.js +0 -9
  63. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +6 -6
  64. package/packages/dd-trace/src/baggage.js +36 -0
  65. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +4 -2
  66. package/packages/dd-trace/src/config.js +14 -2
  67. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +61 -7
  68. package/packages/dd-trace/src/debugger/devtools_client/index.js +10 -26
  69. package/packages/dd-trace/src/debugger/devtools_client/send.js +8 -7
  70. package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +15 -7
  71. package/packages/dd-trace/src/debugger/devtools_client/state.js +22 -2
  72. package/packages/dd-trace/src/dogstatsd.js +2 -0
  73. package/packages/dd-trace/src/exporters/common/docker.js +13 -31
  74. package/packages/dd-trace/src/guardrails/telemetry.js +2 -5
  75. package/packages/dd-trace/src/llmobs/tagger.js +3 -3
  76. package/packages/dd-trace/src/llmobs/writers/base.js +33 -12
  77. package/packages/dd-trace/src/noop/proxy.js +5 -0
  78. package/packages/dd-trace/src/opentelemetry/context_manager.js +2 -0
  79. package/packages/dd-trace/src/opentracing/propagation/text_map.js +17 -9
  80. package/packages/dd-trace/src/plugin_manager.js +2 -0
  81. package/packages/dd-trace/src/plugins/index.js +4 -0
  82. package/packages/dd-trace/src/plugins/log_plugin.js +9 -20
  83. package/packages/dd-trace/src/plugins/outbound.js +11 -3
  84. package/packages/dd-trace/src/plugins/tracing.js +8 -4
  85. package/packages/dd-trace/src/plugins/util/test.js +1 -1
  86. package/packages/dd-trace/src/profiling/exporter_cli.js +1 -1
  87. package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_lookup.js +1 -1
  88. package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_lookupservice.js +1 -1
  89. package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_resolve.js +2 -2
  90. package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_reverse.js +1 -1
  91. package/packages/dd-trace/src/profiling/profilers/event_plugins/event.js +15 -14
  92. package/packages/dd-trace/src/proxy.js +12 -4
  93. package/packages/dd-trace/src/serverless.js +0 -48
  94. package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +8 -0
  95. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +8 -0
  96. package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +8 -0
  97. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +4 -0
  98. package/packages/dd-trace/src/standalone/product.js +3 -5
@@ -26,7 +26,7 @@ const {
26
26
  getOnHookEndHandler,
27
27
  getOnFailHandler,
28
28
  getOnPendingHandler,
29
- testFileToSuiteAr,
29
+ testFileToSuiteCtx,
30
30
  newTests,
31
31
  testsQuarantined,
32
32
  getTestFullName,
@@ -53,7 +53,7 @@ const originalCoverageMap = createCoverageMap()
53
53
  let untestedCoverage
54
54
 
55
55
  // test channels
56
- const testStartCh = channel('ci:mocha:test:start')
56
+ const testFinishCh = channel('ci:mocha:test:finish')
57
57
 
58
58
  // test suite channels
59
59
  const testSuiteStartCh = channel('ci:mocha:test-suite:start')
@@ -166,7 +166,7 @@ function getOnEndHandler (isParallel) {
166
166
  error = new Error(`Failed tests: ${this.failures}.`)
167
167
  }
168
168
 
169
- testFileToSuiteAr.clear()
169
+ testFileToSuiteCtx.clear()
170
170
 
171
171
  let testCodeCoverageLinesTotal
172
172
  if (global.__coverage__) {
@@ -312,7 +312,7 @@ addHook({
312
312
  }, (Mocha) => {
313
313
  shimmer.wrap(Mocha.prototype, 'run', run => function () {
314
314
  // Workers do not need to request any data, just run the tests
315
- if (!testStartCh.hasSubscribers || process.env.MOCHA_WORKER_ID || this.options.parallel) {
315
+ if (!testFinishCh.hasSubscribers || process.env.MOCHA_WORKER_ID || this.options.parallel) {
316
316
  return run.apply(this, arguments)
317
317
  }
318
318
 
@@ -367,7 +367,7 @@ addHook({
367
367
  }, (run) => {
368
368
  // `runMocha` is an async function
369
369
  shimmer.wrap(run, 'runMocha', runMocha => function () {
370
- if (!testStartCh.hasSubscribers) {
370
+ if (!testFinishCh.hasSubscribers) {
371
371
  return runMocha.apply(this, arguments)
372
372
  }
373
373
  const mocha = arguments[0]
@@ -403,7 +403,7 @@ addHook({
403
403
  shimmer.wrap(Runner.prototype, 'runTests', runTests => getRunTestsWrapper(runTests, config))
404
404
 
405
405
  shimmer.wrap(Runner.prototype, 'run', run => function () {
406
- if (!testStartCh.hasSubscribers) {
406
+ if (!testFinishCh.hasSubscribers) {
407
407
  return run.apply(this, arguments)
408
408
  }
409
409
 
@@ -430,20 +430,18 @@ addHook({
430
430
  if (suite.root || !suite.tests.length) {
431
431
  return
432
432
  }
433
- let asyncResource = testFileToSuiteAr.get(suite.file)
434
- if (!asyncResource) {
435
- asyncResource = new AsyncResource('bound-anonymous-fn')
436
- testFileToSuiteAr.set(suite.file, asyncResource)
433
+ let ctx = testFileToSuiteCtx.get(suite.file)
434
+ if (!ctx) {
437
435
  const isUnskippable = unskippableSuites.includes(suite.file)
438
436
  isForcedToRun = isUnskippable && suitesToSkip.includes(getTestSuitePath(suite.file, process.cwd()))
439
- asyncResource.runInAsyncScope(() => {
440
- testSuiteStartCh.publish({
441
- testSuiteAbsolutePath: suite.file,
442
- isUnskippable,
443
- isForcedToRun,
444
- itrCorrelationId
445
- })
446
- })
437
+ ctx = {
438
+ testSuiteAbsolutePath: suite.file,
439
+ isUnskippable,
440
+ isForcedToRun,
441
+ itrCorrelationId
442
+ }
443
+ testFileToSuiteCtx.set(suite.file, ctx)
444
+ testSuiteStartCh.runStores(ctx, () => { })
447
445
  }
448
446
  })
449
447
 
@@ -485,11 +483,9 @@ addHook({
485
483
  resetCoverage(global.__coverage__)
486
484
  }
487
485
 
488
- const asyncResource = testFileToSuiteAr.get(suite.file)
489
- if (asyncResource) {
490
- asyncResource.runInAsyncScope(() => {
491
- testSuiteFinishCh.publish(status)
492
- })
486
+ const ctx = testFileToSuiteCtx.get(suite.file)
487
+ if (ctx) {
488
+ testSuiteFinishCh.publish({ status, ...ctx.currentStore }, () => { })
493
489
  } else {
494
490
  log.warn(() => `No AsyncResource found for suite ${suite.file}`)
495
491
  }
@@ -520,58 +516,49 @@ addHook({
520
516
  file: 'src/WorkerHandler.js'
521
517
  }, (workerHandlerPackage) => {
522
518
  shimmer.wrap(workerHandlerPackage.prototype, 'exec', exec => function (_, path) {
523
- if (!testStartCh.hasSubscribers) {
519
+ if (!testFinishCh.hasSubscribers) {
524
520
  return exec.apply(this, arguments)
525
521
  }
526
522
  if (!path?.length) {
527
523
  return exec.apply(this, arguments)
528
524
  }
529
525
  const [testSuiteAbsolutePath] = path
530
- const testSuiteAsyncResource = new AsyncResource('bound-anonymous-fn')
526
+ const testSuiteContext = { }
531
527
 
532
528
  function onMessage (message) {
533
529
  if (Array.isArray(message)) {
534
530
  const [messageCode, payload] = message
535
531
  if (messageCode === MOCHA_WORKER_TRACE_PAYLOAD_CODE) {
536
- testSuiteAsyncResource.runInAsyncScope(() => {
537
- workerReportTraceCh.publish(payload)
538
- })
532
+ workerReportTraceCh.publish(payload)
539
533
  }
540
534
  }
541
535
  }
542
536
 
543
537
  this.worker.on('message', onMessage)
544
538
 
545
- testSuiteAsyncResource.runInAsyncScope(() => {
546
- testSuiteStartCh.publish({
547
- testSuiteAbsolutePath
548
- })
549
- })
539
+ testSuiteContext.testSuiteAbsolutePath = testSuiteAbsolutePath
540
+ testSuiteStartCh.runStores(testSuiteContext, () => { })
550
541
 
551
542
  try {
552
543
  const promise = exec.apply(this, arguments)
553
544
  promise.then(
554
545
  (result) => {
555
546
  const status = result.failureCount === 0 ? 'pass' : 'fail'
556
- testSuiteAsyncResource.runInAsyncScope(() => {
557
- testSuiteFinishCh.publish(status)
558
- })
547
+ testSuiteFinishCh.publish({ status, ...testSuiteContext.currentStore }, () => { })
559
548
  this.worker.off('message', onMessage)
560
549
  },
561
550
  (err) => {
562
- testSuiteAsyncResource.runInAsyncScope(() => {
563
- testSuiteErrorCh.publish(err)
564
- testSuiteFinishCh.publish('fail')
565
- })
551
+ testSuiteContext.error = err
552
+ testSuiteErrorCh.runStores(testSuiteContext, () => { })
553
+ testSuiteFinishCh.publish({ status: 'fail', ...testSuiteContext.currentStore }, () => { })
566
554
  this.worker.off('message', onMessage)
567
555
  }
568
556
  )
569
557
  return promise
570
558
  } catch (err) {
571
- testSuiteAsyncResource.runInAsyncScope(() => {
572
- testSuiteErrorCh.publish(err)
573
- testSuiteFinishCh.publish('fail')
574
- })
559
+ testSuiteContext.error = err
560
+ testSuiteErrorCh.runStores(testSuiteContext, () => { })
561
+ testSuiteFinishCh.publish({ status: 'fail', ...testSuiteContext.currentStore }, () => { })
575
562
  this.worker.off('message', onMessage)
576
563
  throw err
577
564
  }
@@ -588,7 +575,7 @@ addHook({
588
575
  file: 'lib/nodejs/parallel-buffered-runner.js'
589
576
  }, (ParallelBufferedRunner, frameworkVersion) => {
590
577
  shimmer.wrap(ParallelBufferedRunner.prototype, 'run', run => function (cb, { files }) {
591
- if (!testStartCh.hasSubscribers) {
578
+ if (!testFinishCh.hasSubscribers) {
592
579
  return run.apply(this, arguments)
593
580
  }
594
581
 
@@ -629,7 +616,7 @@ addHook({
629
616
  const { BufferedWorkerPool } = BufferedWorkerPoolPackage
630
617
 
631
618
  shimmer.wrap(BufferedWorkerPool.prototype, 'run', run => async function (testSuiteAbsolutePath, workerArgs) {
632
- if (!testStartCh.hasSubscribers || (!config.isKnownTestsEnabled && !config.isTestManagementTestsEnabled)) {
619
+ if (!testFinishCh.hasSubscribers || (!config.isKnownTestsEnabled && !config.isTestManagementTestsEnabled)) {
633
620
  return run.apply(this, arguments)
634
621
  }
635
622
 
@@ -7,7 +7,7 @@ const {
7
7
  addAttemptToFixStringToTestName,
8
8
  removeAttemptToFixStringFromTestName
9
9
  } = require('../../../dd-trace/src/plugins/util/test')
10
- const { channel, AsyncResource } = require('../helpers/instrument')
10
+ const { channel } = require('../helpers/instrument')
11
11
  const shimmer = require('../../../datadog-shimmer')
12
12
 
13
13
  // test channels
@@ -17,15 +17,16 @@ const testFinishCh = channel('ci:mocha:test:finish')
17
17
  const testRetryCh = channel('ci:mocha:test:retry')
18
18
  const errorCh = channel('ci:mocha:test:error')
19
19
  const skipCh = channel('ci:mocha:test:skip')
20
+ const testFnCh = channel('ci:mocha:test:fn')
20
21
 
21
22
  // suite channels
22
23
  const testSuiteErrorCh = channel('ci:mocha:test-suite:error')
23
24
 
24
25
  const BREAKPOINT_HIT_GRACE_PERIOD_MS = 200
25
- const testToAr = new WeakMap()
26
+ const testToContext = new WeakMap()
26
27
  const originalFns = new WeakMap()
27
28
  const testToStartLine = new WeakMap()
28
- const testFileToSuiteAr = new Map()
29
+ const testFileToSuiteCtx = new Map()
29
30
  const wrappedFunctions = new WeakSet()
30
31
  const newTests = {}
31
32
  const testsAttemptToFix = new Set()
@@ -125,7 +126,7 @@ function getTestStatus (test) {
125
126
  return 'pass'
126
127
  }
127
128
 
128
- function getTestToArKey (test) {
129
+ function getTestToContextKey (test) {
129
130
  if (!test.fn) {
130
131
  return test
131
132
  }
@@ -136,14 +137,14 @@ function getTestToArKey (test) {
136
137
  return originalFn
137
138
  }
138
139
 
139
- function getTestAsyncResource (test) {
140
- const key = getTestToArKey(test)
141
- return testToAr.get(key)
140
+ function getTestContext (test) {
141
+ const key = getTestToContextKey(test)
142
+ return testToContext.get(key)
142
143
  }
143
144
 
144
145
  function runnableWrapper (RunnablePackage, libraryConfig) {
145
146
  shimmer.wrap(RunnablePackage.prototype, 'run', run => function () {
146
- if (!testStartCh.hasSubscribers) {
147
+ if (!testFinishCh.hasSubscribers) {
147
148
  return run.apply(this, arguments)
148
149
  }
149
150
  // Flaky test retries does not work in parallel mode
@@ -167,14 +168,17 @@ function runnableWrapper (RunnablePackage, libraryConfig) {
167
168
 
168
169
  if (isTestHook || this.type === 'test') {
169
170
  const test = isTestHook ? this.ctx.currentTest : this
170
- const asyncResource = getTestAsyncResource(test)
171
+ const ctx = getTestContext(test)
171
172
 
172
- if (asyncResource) {
173
- // we bind the test fn to the correct async resource
174
- const newFn = asyncResource.bind(this.fn)
173
+ if (ctx) {
174
+ const originalFn = this.fn
175
+ // we bind the test fn to the correct context
176
+ const newFn = function () {
177
+ return testFnCh.runStores(ctx, () => originalFn.apply(this, arguments))
178
+ }
175
179
 
176
180
  // we store the original function, not to lose it
177
- originalFns.set(newFn, this.fn)
181
+ originalFns.set(newFn, originalFn)
178
182
  this.fn = newFn
179
183
 
180
184
  wrappedFunctions.add(this.fn)
@@ -189,7 +193,6 @@ function runnableWrapper (RunnablePackage, libraryConfig) {
189
193
  function getOnTestHandler (isMain) {
190
194
  return function (test) {
191
195
  const testStartLine = testToStartLine.get(test)
192
- const asyncResource = new AsyncResource('bound-anonymous-fn')
193
196
 
194
197
  // This may be a retry. If this is the case, `test.fn` is already wrapped,
195
198
  // so we need to restore it.
@@ -198,7 +201,6 @@ function getOnTestHandler (isMain) {
198
201
  test.fn = originalFn
199
202
  wrappedFunctions.delete(test.fn)
200
203
  }
201
- testToAr.set(test.fn, asyncResource)
202
204
 
203
205
  const {
204
206
  file: testSuiteAbsolutePath,
@@ -242,15 +244,15 @@ function getOnTestHandler (isMain) {
242
244
  test.pending = true
243
245
  }
244
246
 
245
- asyncResource.runInAsyncScope(() => {
246
- testStartCh.publish(testInfo)
247
- })
247
+ const ctx = testInfo
248
+ testToContext.set(test.fn, ctx)
249
+ testStartCh.runStores(ctx, () => { })
248
250
  }
249
251
  }
250
252
 
251
253
  function getOnTestEndHandler (config) {
252
254
  return async function (test) {
253
- const asyncResource = getTestAsyncResource(test)
255
+ const ctx = getTestContext(test)
254
256
  const status = getTestStatus(test)
255
257
 
256
258
  // After finishing it might take a bit for the snapshot to be handled.
@@ -265,6 +267,7 @@ function getOnTestEndHandler (config) {
265
267
 
266
268
  let hasFailedAllRetries = false
267
269
  let attemptToFixPassed = false
270
+ let attemptToFixFailed = false
268
271
 
269
272
  const testName = getTestFullName(test)
270
273
 
@@ -278,6 +281,9 @@ function getOnTestEndHandler (config) {
278
281
  const isLastAttempt = testStatuses.length === config.testManagementAttemptToFixRetries + 1
279
282
 
280
283
  if (test._ddIsAttemptToFix && isLastAttempt) {
284
+ if (testStatuses.some(status => status === 'fail')) {
285
+ attemptToFixFailed = true
286
+ }
281
287
  if (testStatuses.every(status => status === 'fail')) {
282
288
  hasFailedAllRetries = true
283
289
  } else if (testStatuses.every(status => status === 'pass')) {
@@ -291,17 +297,17 @@ function getOnTestEndHandler (config) {
291
297
  !test._ddIsEfdRetry
292
298
 
293
299
  // if there are afterEach to be run, we don't finish the test yet
294
- if (asyncResource && !getAfterEachHooks(test).length) {
295
- asyncResource.runInAsyncScope(() => {
296
- testFinishCh.publish({
297
- status,
298
- hasBeenRetried: isMochaRetry(test),
299
- isLastRetry: getIsLastRetry(test),
300
- hasFailedAllRetries,
301
- attemptToFixPassed,
302
- isAttemptToFixRetry,
303
- isAtrRetry
304
- })
300
+ if (ctx && !getAfterEachHooks(test).length) {
301
+ testFinishCh.publish({
302
+ status,
303
+ hasBeenRetried: isMochaRetry(test),
304
+ isLastRetry: getIsLastRetry(test),
305
+ hasFailedAllRetries,
306
+ attemptToFixPassed,
307
+ attemptToFixFailed,
308
+ isAttemptToFixRetry,
309
+ isAtrRetry,
310
+ ...ctx.currentStore
305
311
  })
306
312
  }
307
313
  }
@@ -315,10 +321,13 @@ function getOnHookEndHandler () {
315
321
  const isLastAfterEach = afterEachHooks.indexOf(hook) === afterEachHooks.length - 1
316
322
  if (isLastAfterEach) {
317
323
  const status = getTestStatus(test)
318
- const asyncResource = getTestAsyncResource(test)
319
- if (asyncResource) {
320
- asyncResource.runInAsyncScope(() => {
321
- testFinishCh.publish({ status, hasBeenRetried: isMochaRetry(test), isLastRetry: getIsLastRetry(test) })
324
+ const ctx = getTestContext(test)
325
+ if (ctx) {
326
+ testFinishCh.publish({
327
+ status,
328
+ hasBeenRetried: isMochaRetry(test),
329
+ isLastRetry: getIsLastRetry(test),
330
+ ...ctx.currentStore
322
331
  })
323
332
  }
324
333
  }
@@ -334,35 +343,34 @@ function getOnFailHandler (isMain) {
334
343
  if (isHook && testOrHook.ctx) {
335
344
  test = testOrHook.ctx.currentTest
336
345
  }
337
- let testAsyncResource
346
+ let testContext
338
347
  if (test) {
339
- testAsyncResource = getTestAsyncResource(test)
348
+ testContext = getTestContext(test)
340
349
  }
341
- if (testAsyncResource) {
342
- testAsyncResource.runInAsyncScope(() => {
343
- if (isHook) {
344
- err.message = `${testOrHook.fullTitle()}: ${err.message}`
345
- errorCh.publish(err)
346
- // if it's a hook and it has failed, 'test end' will not be called
347
- testFinishCh.publish({ status: 'fail', hasBeenRetried: isMochaRetry(test) })
348
- } else {
349
- errorCh.publish(err)
350
- }
351
- })
350
+ if (testContext) {
351
+ if (isHook) {
352
+ err.message = `${testOrHook.fullTitle()}: ${err.message}`
353
+ testContext.err = err
354
+ errorCh.runStores(testContext, () => { })
355
+ // if it's a hook and it has failed, 'test end' will not be called
356
+ testFinishCh.publish({ status: 'fail', hasBeenRetried: isMochaRetry(test), ...testContext.currentStore })
357
+ } else {
358
+ testContext.err = err
359
+ errorCh.runStores(testContext, () => { })
360
+ }
352
361
  }
353
362
 
354
363
  if (isMain) {
355
- const testSuiteAsyncResource = testFileToSuiteAr.get(testFile)
364
+ const testSuiteContext = testFileToSuiteCtx.get(testFile)
356
365
 
357
- if (testSuiteAsyncResource) {
366
+ if (testSuiteContext) {
358
367
  // we propagate the error to the suite
359
368
  const testSuiteError = new Error(
360
369
  `"${testOrHook.parent.fullTitle()}" failed with message "${err.message}"`
361
370
  )
362
371
  testSuiteError.stack = err.stack
363
- testSuiteAsyncResource.runInAsyncScope(() => {
364
- testSuiteErrorCh.publish(testSuiteError)
365
- })
372
+ testSuiteContext.error = testSuiteError
373
+ testSuiteErrorCh.runStores(testSuiteContext, () => { })
366
374
  }
367
375
  }
368
376
  }
@@ -370,20 +378,18 @@ function getOnFailHandler (isMain) {
370
378
 
371
379
  function getOnTestRetryHandler (config) {
372
380
  return function (test, err) {
373
- const asyncResource = getTestAsyncResource(test)
374
- if (asyncResource) {
381
+ const ctx = getTestContext(test)
382
+ if (ctx) {
375
383
  const isFirstAttempt = test._currentRetry === 0
376
384
  const willBeRetried = test._currentRetry < test._retries
377
385
  const isAtrRetry = !isFirstAttempt &&
378
386
  config.isFlakyTestRetriesEnabled &&
379
387
  !test._ddIsAttemptToFix &&
380
388
  !test._ddIsEfdRetry
381
- asyncResource.runInAsyncScope(() => {
382
- testRetryCh.publish({ isFirstAttempt, err, willBeRetried, test, isAtrRetry })
383
- })
389
+ testRetryCh.publish({ isFirstAttempt, err, willBeRetried, test, isAtrRetry, ...ctx.currentStore })
384
390
  }
385
- const key = getTestToArKey(test)
386
- testToAr.delete(key)
391
+ const key = getTestToContextKey(test)
392
+ testToContext.delete(key)
387
393
  }
388
394
  }
389
395
 
@@ -402,23 +408,19 @@ function getOnPendingHandler () {
402
408
  testStartLine
403
409
  }
404
410
 
405
- const asyncResource = getTestAsyncResource(test)
406
- if (asyncResource) {
407
- asyncResource.runInAsyncScope(() => {
408
- skipCh.publish(testInfo)
409
- })
411
+ const ctx = getTestContext(test)
412
+ if (ctx) {
413
+ skipCh.publish(testInfo)
410
414
  } else {
411
- // if there is no async resource, the test has been skipped through `test.skip`
415
+ // if there is no context, the test has been skipped through `test.skip`
412
416
  // or the parent suite is skipped
413
- const skippedTestAsyncResource = new AsyncResource('bound-anonymous-fn')
417
+ const testCtx = testInfo
414
418
  if (test.fn) {
415
- testToAr.set(test.fn, skippedTestAsyncResource)
419
+ testToContext.set(test.fn, testCtx)
416
420
  } else {
417
- testToAr.set(test, skippedTestAsyncResource)
421
+ testToContext.set(test, testCtx)
418
422
  }
419
- skippedTestAsyncResource.runInAsyncScope(() => {
420
- skipCh.publish(testInfo)
421
- })
423
+ skipCh.runStores(testCtx, () => { })
422
424
  }
423
425
  }
424
426
  }
@@ -479,9 +481,9 @@ module.exports = {
479
481
  getTestFullName,
480
482
  getTestStatus,
481
483
  runnableWrapper,
482
- testToAr,
484
+ testToContext,
483
485
  originalFns,
484
- getTestAsyncResource,
486
+ getTestContext,
485
487
  testToStartLine,
486
488
  getOnTestHandler,
487
489
  getOnTestEndHandler,
@@ -489,7 +491,7 @@ module.exports = {
489
491
  getOnHookEndHandler,
490
492
  getOnFailHandler,
491
493
  getOnPendingHandler,
492
- testFileToSuiteAr,
494
+ testFileToSuiteCtx,
493
495
  getRunTestsWrapper,
494
496
  newTests,
495
497
  testsQuarantined,
@@ -1,5 +1,7 @@
1
1
  'use strict'
2
2
 
3
+ const { errorMonitor } = require('node:events')
4
+
3
5
  const {
4
6
  channel,
5
7
  addHook,
@@ -138,7 +140,7 @@ function wrapConnection (Connection, version) {
138
140
  onResult.apply(this, arguments)
139
141
  }, 'bound-anonymous-fn', this))
140
142
  } else {
141
- this.on('error', asyncResource.bind(error => errorCh.publish(error)))
143
+ this.on(errorMonitor, asyncResource.bind(error => errorCh.publish(error)))
142
144
  this.on('end', asyncResource.bind(() => finishCh.publish(undefined)))
143
145
  }
144
146
 
@@ -1,10 +1,8 @@
1
1
  'use strict'
2
2
 
3
- const {
4
- channel,
5
- addHook,
6
- AsyncResource
7
- } = require('./helpers/instrument')
3
+ const { errorMonitor } = require('events')
4
+
5
+ const { channel, addHook } = require('./helpers/instrument')
8
6
  const shimmer = require('../../datadog-shimmer')
9
7
 
10
8
  const startICPCh = channel('apm:net:ipc:start')
@@ -15,6 +13,7 @@ const startTCPCh = channel('apm:net:tcp:start')
15
13
  const finishTCPCh = channel('apm:net:tcp:finish')
16
14
  const errorTCPCh = channel('apm:net:tcp:error')
17
15
 
16
+ const readyCh = channel('apm:net:tcp:ready')
18
17
  const connectionCh = channel('apm:net:tcp:connection')
19
18
 
20
19
  const names = ['net', 'node:net']
@@ -39,30 +38,27 @@ addHook({ name: names }, (net, version, name) => {
39
38
 
40
39
  if (!options) return connect.apply(this, arguments)
41
40
 
42
- const callbackResource = new AsyncResource('bound-anonymous-fn')
43
- const asyncResource = new AsyncResource('bound-anonymous-fn')
41
+ const protocol = options.path ? 'ipc' : 'tcp'
42
+ const startCh = protocol === 'ipc' ? startICPCh : startTCPCh
43
+ const finishCh = protocol === 'ipc' ? finishICPCh : finishTCPCh
44
+ const errorCh = protocol === 'ipc' ? errorICPCh : errorTCPCh
45
+ const ctx = { options }
44
46
 
45
47
  if (typeof callback === 'function') {
46
- arguments[lastIndex] = callbackResource.bind(callback)
48
+ arguments[lastIndex] = function (...args) {
49
+ return finishCh.runStores(ctx, callback, this, ...args)
50
+ }
47
51
  }
48
52
 
49
- const protocol = options.path ? 'ipc' : 'tcp'
50
-
51
- return asyncResource.runInAsyncScope(() => {
52
- if (protocol === 'ipc') {
53
- startICPCh.publish({ options })
54
- setupListeners(this, 'ipc', asyncResource)
55
- } else {
56
- startTCPCh.publish({ options })
57
- setupListeners(this, 'tcp', asyncResource)
58
- }
53
+ return startCh.runStores(ctx, () => {
54
+ setupListeners(this, protocol, ctx, finishCh, errorCh)
59
55
 
60
56
  const emit = this.emit
61
57
  this.emit = shimmer.wrapFunction(emit, emit => function (eventName) {
62
58
  switch (eventName) {
63
59
  case 'ready':
64
60
  case 'connect':
65
- return callbackResource.runInAsyncScope(() => {
61
+ return readyCh.runStores(ctx, () => {
66
62
  return emit.apply(this, arguments)
67
63
  })
68
64
  default:
@@ -73,7 +69,7 @@ addHook({ name: names }, (net, version, name) => {
73
69
  try {
74
70
  return connect.apply(this, arguments)
75
71
  } catch (err) {
76
- protocol === 'ipc' ? errorICPCh.publish(err) : errorTCPCh.publish(err)
72
+ errorCh.publish(err)
77
73
 
78
74
  throw err
79
75
  }
@@ -104,19 +100,21 @@ function getOptions (args) {
104
100
  }
105
101
  }
106
102
 
107
- function setupListeners (socket, protocol, asyncResource) {
108
- const events = ['connect', 'error', 'close', 'timeout']
103
+ function setupListeners (socket, protocol, ctx, finishCh, errorCh) {
104
+ const events = ['connect', errorMonitor, 'close', 'timeout']
109
105
 
110
- const wrapListener = asyncResource.bind(function (error) {
106
+ const wrapListener = function (error) {
111
107
  if (error) {
112
- protocol === 'ipc' ? errorICPCh.publish(error) : errorTCPCh.publish(error)
108
+ ctx.error = error
109
+ errorCh.publish(ctx)
113
110
  }
114
- protocol === 'ipc' ? finishICPCh.publish(undefined) : finishTCPCh.publish(undefined)
115
- })
111
+ finishCh.runStores(ctx, () => {})
112
+ }
116
113
 
117
- const localListener = asyncResource.bind(function () {
118
- connectionCh.publish({ socket })
119
- })
114
+ const localListener = function () {
115
+ ctx.socket = socket
116
+ connectionCh.publish(ctx)
117
+ }
120
118
 
121
119
  const cleanupListener = function () {
122
120
  socket.removeListener('connect', localListener)