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
@@ -70,7 +70,7 @@ function wrapRenderToHTML (renderToHTML) {
70
70
 
71
71
  function wrapRenderErrorToHTML (renderErrorToHTML) {
72
72
  return function (err, req, res, pathname, query) {
73
- return instrument(req, res, err, () => renderErrorToHTML.apply(this, arguments))
73
+ return instrument(req, res, () => renderErrorToHTML.apply(this, arguments), err)
74
74
  }
75
75
  }
76
76
 
@@ -82,7 +82,7 @@ function wrapRenderToResponse (renderToResponse) {
82
82
 
83
83
  function wrapRenderErrorToResponse (renderErrorToResponse) {
84
84
  return function (ctx, err) {
85
- return instrument(ctx.req, ctx.res, err, () => renderErrorToResponse.apply(this, arguments))
85
+ return instrument(ctx.req, ctx.res, () => renderErrorToResponse.apply(this, arguments), err)
86
86
  }
87
87
  }
88
88
 
@@ -121,12 +121,7 @@ function getRequestMeta (req, key) {
121
121
  return typeof key === 'string' ? meta[key] : meta
122
122
  }
123
123
 
124
- function instrument (req, res, error, handler) {
125
- if (typeof error === 'function') {
126
- handler = error
127
- error = null
128
- }
129
-
124
+ function instrument (req, res, handler, error) {
130
125
  req = req.originalRequest || req
131
126
  res = res.originalResponse || res
132
127
 
@@ -216,13 +211,13 @@ addHook({
216
211
  name: 'next',
217
212
  versions: ['>=11.1'],
218
213
  file: 'dist/server/serve-static.js'
219
- }, serveStatic => shimmer.wrap(serveStatic, 'serveStatic', wrapServeStatic))
214
+ }, serveStatic => shimmer.wrap(serveStatic, 'serveStatic', wrapServeStatic, { replaceGetter: true }))
220
215
 
221
216
  addHook({
222
217
  name: 'next',
223
218
  versions: ['>=10.2 <11.1'],
224
219
  file: 'dist/next-server/server/serve-static.js'
225
- }, serveStatic => shimmer.wrap(serveStatic, 'serveStatic', wrapServeStatic))
220
+ }, serveStatic => shimmer.wrap(serveStatic, 'serveStatic', wrapServeStatic, { replaceGetter: true }))
226
221
 
227
222
  addHook({ name: 'next', versions: ['>=11.1'], file: 'dist/server/next-server.js' }, nextServer => {
228
223
  const Server = nextServer.default
@@ -282,8 +277,7 @@ addHook({
282
277
  versions: ['>=13'],
283
278
  file: 'dist/server/web/spec-extension/request.js'
284
279
  }, request => {
285
- const nextUrlDescriptor = Object.getOwnPropertyDescriptor(request.NextRequest.prototype, 'nextUrl')
286
- shimmer.wrap(nextUrlDescriptor, 'get', function (originalGet) {
280
+ shimmer.wrap(request.NextRequest.prototype, 'nextUrl', function (originalGet) {
287
281
  return function wrappedGet () {
288
282
  const nextUrl = originalGet.apply(this, arguments)
289
283
  if (queryParsedChannel.hasSubscribers) {
@@ -300,8 +294,6 @@ addHook({
300
294
  }
301
295
  })
302
296
 
303
- Object.defineProperty(request.NextRequest.prototype, 'nextUrl', nextUrlDescriptor)
304
-
305
297
  shimmer.massWrap(request.NextRequest.prototype, ['text', 'json'], function (originalMethod) {
306
298
  return async function wrappedJson () {
307
299
  const body = await originalMethod.apply(this, arguments)
@@ -14,6 +14,8 @@ const errorCh = channel('apm:pg:query:error')
14
14
  const startPoolQueryCh = channel('datadog:pg:pool:query:start')
15
15
  const finishPoolQueryCh = channel('datadog:pg:pool:query:finish')
16
16
 
17
+ const { errorMonitor } = require('node:events')
18
+
17
19
  addHook({ name: 'pg', versions: ['>=8.0.3'] }, pg => {
18
20
  shimmer.wrap(pg.Client.prototype, 'query', query => wrapQuery(query))
19
21
  shimmer.wrap(pg.Pool.prototype, 'query', query => wrapPoolQuery(query))
@@ -39,13 +41,15 @@ function wrapQuery (query) {
39
41
  ? arguments[0]
40
42
  : { text: arguments[0] }
41
43
 
42
- const textProp = Object.getOwnPropertyDescriptor(pgQuery, 'text')
44
+ const textPropObj = pgQuery.cursor ?? pgQuery
45
+ const textProp = Object.getOwnPropertyDescriptor(textPropObj, 'text')
46
+ const stream = typeof textPropObj.read === 'function'
43
47
 
44
- // Only alter `text` property if safe to do so.
48
+ // Only alter `text` property if safe to do so. Initially, it's a property, not a getter.
45
49
  if (!textProp || textProp.configurable) {
46
- const originalText = pgQuery.text
50
+ const originalText = textPropObj.text
47
51
 
48
- Object.defineProperty(pgQuery, 'text', {
52
+ Object.defineProperty(textPropObj, 'text', {
49
53
  get () {
50
54
  return this?.__ddInjectableQuery || originalText
51
55
  }
@@ -57,9 +61,10 @@ function wrapQuery (query) {
57
61
 
58
62
  startCh.publish({
59
63
  params: this.connectionParameters,
60
- query: pgQuery,
64
+ query: textPropObj,
61
65
  processId,
62
- abortController
66
+ abortController,
67
+ stream
63
68
  })
64
69
 
65
70
  const finish = asyncResource.bind(function (error, res) {
@@ -124,9 +129,12 @@ function wrapQuery (query) {
124
129
  }
125
130
  } else if (newQuery.once) {
126
131
  newQuery
127
- .once('error', finish)
132
+ .once(errorMonitor, finish)
128
133
  .once('end', (res) => finish(null, res))
129
134
  } else {
135
+ // TODO: This code is never reached in our tests.
136
+ // Internally, pg always uses callbacks or streams, even for promise based queries.
137
+ // Investigate if this code should just be removed.
130
138
  newQuery.then((res) => finish(null, res), finish)
131
139
  }
132
140
 
@@ -25,8 +25,8 @@ const testSuiteFinishCh = channel('ci:playwright:test-suite:finish')
25
25
  const workerReportCh = channel('ci:playwright:worker:report')
26
26
  const testPageGotoCh = channel('ci:playwright:test:page-goto')
27
27
 
28
- const testToAr = new WeakMap()
29
- const testSuiteToAr = new Map()
28
+ const testToCtx = new WeakMap()
29
+ const testSuiteToCtx = new Map()
30
30
  const testSuiteToTestStatuses = new Map()
31
31
  const testSuiteToErrors = new Map()
32
32
  const testsToTestStatuses = new Map()
@@ -279,11 +279,9 @@ function testBeginHandler (test, browserName, isMainProcess) {
279
279
 
280
280
  if (isNewTestSuite) {
281
281
  startedSuites.push(testSuiteAbsolutePath)
282
- const testSuiteAsyncResource = new AsyncResource('bound-anonymous-fn')
283
- testSuiteToAr.set(testSuiteAbsolutePath, testSuiteAsyncResource)
284
- testSuiteAsyncResource.runInAsyncScope(() => {
285
- testSuiteStartCh.publish(testSuiteAbsolutePath)
286
- })
282
+ const testSuiteCtx = { testSuiteAbsolutePath }
283
+ testSuiteToCtx.set(testSuiteAbsolutePath, testSuiteCtx)
284
+ testSuiteStartCh.runStores(testSuiteCtx, () => { })
287
285
  }
288
286
 
289
287
  // We disable retries by default if attemptToFix is true
@@ -293,19 +291,17 @@ function testBeginHandler (test, browserName, isMainProcess) {
293
291
 
294
292
  // this handles tests that do not go through the worker process (because they're skipped)
295
293
  if (isMainProcess) {
296
- const testAsyncResource = new AsyncResource('bound-anonymous-fn')
297
- testToAr.set(test, testAsyncResource)
298
294
  const testName = getTestFullname(test)
295
+ const testCtx = {
296
+ testName,
297
+ testSuiteAbsolutePath,
298
+ testSourceLine,
299
+ browserName,
300
+ isDisabled: test._ddIsDisabled
301
+ }
302
+ testToCtx.set(test, testCtx)
299
303
 
300
- testAsyncResource.runInAsyncScope(() => {
301
- testStartCh.publish({
302
- testName,
303
- testSuiteAbsolutePath,
304
- testSourceLine,
305
- browserName,
306
- isDisabled: test._ddIsDisabled
307
- })
308
- })
304
+ testStartCh.runStores(testCtx, () => { })
309
305
  }
310
306
  }
311
307
 
@@ -337,6 +333,9 @@ function testEndHandler (test, annotations, testStatus, error, isTimeout, isMain
337
333
  }
338
334
 
339
335
  if (testStatuses.length === testManagementAttemptToFixRetries + 1) {
336
+ if (testStatuses.some(status => status === 'fail')) {
337
+ test._ddHasFailedAttemptToFixRetries = true
338
+ }
340
339
  if (testStatuses.every(status => status === 'fail')) {
341
340
  test._ddHasFailedAllRetries = true
342
341
  } else if (testStatuses.every(status => status === 'pass')) {
@@ -347,27 +346,27 @@ function testEndHandler (test, annotations, testStatus, error, isTimeout, isMain
347
346
  // this handles tests that do not go through the worker process (because they're skipped)
348
347
  if (isMainProcess) {
349
348
  const testResult = results[results.length - 1]
350
- const testAsyncResource = testToAr.get(test)
349
+ const testCtx = testToCtx.get(test)
351
350
  const isAtrRetry = testResult?.retry > 0 &&
352
351
  isFlakyTestRetriesEnabled &&
353
352
  !test._ddIsAttemptToFix &&
354
353
  !test._ddIsEfdRetry
355
- testAsyncResource.runInAsyncScope(() => {
356
- testFinishCh.publish({
357
- testStatus,
358
- steps: testResult?.steps || [],
359
- isRetry: testResult?.retry > 0,
360
- error,
361
- extraTags: annotationTags,
362
- isNew: test._ddIsNew,
363
- isAttemptToFix: test._ddIsAttemptToFix,
364
- isAttemptToFixRetry: test._ddIsAttemptToFixRetry,
365
- isQuarantined: test._ddIsQuarantined,
366
- isEfdRetry: test._ddIsEfdRetry,
367
- hasFailedAllRetries: test._ddHasFailedAllRetries,
368
- hasPassedAttemptToFixRetries: test._ddHasPassedAttemptToFixRetries,
369
- isAtrRetry
370
- })
354
+ testFinishCh.publish({
355
+ testStatus,
356
+ steps: testResult?.steps || [],
357
+ isRetry: testResult?.retry > 0,
358
+ error,
359
+ extraTags: annotationTags,
360
+ isNew: test._ddIsNew,
361
+ isAttemptToFix: test._ddIsAttemptToFix,
362
+ isAttemptToFixRetry: test._ddIsAttemptToFixRetry,
363
+ isQuarantined: test._ddIsQuarantined,
364
+ isEfdRetry: test._ddIsEfdRetry,
365
+ hasFailedAllRetries: test._ddHasFailedAllRetries,
366
+ hasPassedAttemptToFixRetries: test._ddHasPassedAttemptToFixRetries,
367
+ hasFailedAttemptToFixRetries: test._ddHasFailedAttemptToFixRetries,
368
+ isAtrRetry,
369
+ ...testCtx.currentStore
371
370
  })
372
371
  }
373
372
 
@@ -397,10 +396,8 @@ function testEndHandler (test, annotations, testStatus, error, isTimeout, isMain
397
396
  }
398
397
 
399
398
  const suiteError = getTestSuiteError(testSuiteAbsolutePath)
400
- const testSuiteAsyncResource = testSuiteToAr.get(testSuiteAbsolutePath)
401
- testSuiteAsyncResource.runInAsyncScope(() => {
402
- testSuiteFinishCh.publish({ status: testSuiteStatus, error: suiteError })
403
- })
399
+ const testSuiteCtx = testSuiteToCtx.get(testSuiteAbsolutePath)
400
+ testSuiteFinishCh.publish({ status: testSuiteStatus, error: suiteError, ...testSuiteCtx.currentStore })
404
401
  }
405
402
  }
406
403
 
@@ -492,6 +489,7 @@ function dispatcherHookNew (dispatcherExport, runWrapper) {
492
489
  _ddIsEfdRetry: test._ddIsEfdRetry,
493
490
  _ddHasFailedAllRetries: test._ddHasFailedAllRetries,
494
491
  _ddHasPassedAttemptToFixRetries: test._ddHasPassedAttemptToFixRetries,
492
+ _ddHasFailedAttemptToFixRetries: test._ddHasFailedAttemptToFixRetries,
495
493
  _ddIsAtrRetry: isAtrRetry
496
494
  }
497
495
  })
@@ -605,7 +603,7 @@ function runnerHook (runnerExport, playwrightVersion) {
605
603
  totalAttemptToFixFailedTestCount += testStatuses.filter(status => status === 'fail').length
606
604
  }
607
605
 
608
- if (totalFailedTestCount === totalAttemptToFixFailedTestCount) {
606
+ if (totalFailedTestCount > 0 && totalFailedTestCount === totalAttemptToFixFailedTestCount) {
609
607
  runAllTestsReturn = 'passed'
610
608
  }
611
609
  }
@@ -868,17 +866,16 @@ addHook({
868
866
  // If test events are created in the worker process I need to stop creating it in the main process
869
867
  // Probably yet another test worker exporter is needed in addition to the ones for mocha, jest and cucumber
870
868
  // it's probably hard to tell that's a playwright worker though, as I don't think there is a specific env variable
871
- const testAsyncResource = new AsyncResource('bound-anonymous-fn')
869
+ const testCtx = {
870
+ testName,
871
+ testSuiteAbsolutePath,
872
+ testSourceLine,
873
+ browserName
874
+ }
875
+ testToCtx.set(test, testCtx)
872
876
  // TODO - In the future we may need to implement a mechanism to send test properties
873
877
  // to the worker process before _runTest is called
874
- testAsyncResource.runInAsyncScope(() => {
875
- testStartCh.publish({
876
- testName,
877
- testSuiteAbsolutePath,
878
- testSourceLine,
879
- browserName
880
- })
881
-
878
+ testStartCh.runStores(testCtx, () => {
882
879
  let existAfterEachHook = false
883
880
 
884
881
  // We try to find an existing afterEach hook with _ddHook to avoid adding a new one
@@ -971,24 +968,24 @@ addHook({
971
968
  // Wait for the properties to be received
972
969
  await ddPropertiesPromise
973
970
 
974
- testAsyncResource.runInAsyncScope(() => {
975
- testFinishCh.publish({
976
- testStatus: STATUS_TO_TEST_STATUS[status],
977
- steps: steps.filter(step => step.testId === testId),
978
- error,
979
- extraTags: annotationTags,
980
- isNew: test._ddIsNew,
981
- isRetry: retry > 0,
982
- isEfdRetry: test._ddIsEfdRetry,
983
- isAttemptToFix: test._ddIsAttemptToFix,
984
- isDisabled: test._ddIsDisabled,
985
- isQuarantined: test._ddIsQuarantined,
986
- isAttemptToFixRetry: test._ddIsAttemptToFixRetry,
987
- hasFailedAllRetries: test._ddHasFailedAllRetries,
988
- hasPassedAttemptToFixRetries: test._ddHasPassedAttemptToFixRetries,
989
- isAtrRetry: test._ddIsAtrRetry,
990
- onDone
991
- })
971
+ testFinishCh.publish({
972
+ testStatus: STATUS_TO_TEST_STATUS[status],
973
+ steps: steps.filter(step => step.testId === testId),
974
+ error,
975
+ extraTags: annotationTags,
976
+ isNew: test._ddIsNew,
977
+ isRetry: retry > 0,
978
+ isEfdRetry: test._ddIsEfdRetry,
979
+ isAttemptToFix: test._ddIsAttemptToFix,
980
+ isDisabled: test._ddIsDisabled,
981
+ isQuarantined: test._ddIsQuarantined,
982
+ isAttemptToFixRetry: test._ddIsAttemptToFixRetry,
983
+ hasFailedAllRetries: test._ddHasFailedAllRetries,
984
+ hasPassedAttemptToFixRetries: test._ddHasPassedAttemptToFixRetries,
985
+ hasFailedAttemptToFixRetries: test._ddHasFailedAttemptToFixRetries,
986
+ isAtrRetry: test._ddIsAtrRetry,
987
+ onDone,
988
+ ...testCtx.currentStore
992
989
  })
993
990
 
994
991
  await flushPromise
@@ -26,23 +26,17 @@ addHook({ name: names }, function (url) {
26
26
 
27
27
  const URLPrototype = url.URL.prototype.constructor.prototype
28
28
  instrumentedGetters.forEach(property => {
29
- const originalDescriptor = Object.getOwnPropertyDescriptor(URLPrototype, property)
29
+ shimmer.wrap(URLPrototype, property, function (originalGet) {
30
+ return function get () {
31
+ const result = originalGet.call(this)
32
+ if (!urlGetterChannel.hasSubscribers) return result
30
33
 
31
- if (originalDescriptor?.get) {
32
- const newDescriptor = shimmer.wrap(originalDescriptor, 'get', function (originalGet) {
33
- return function get () {
34
- const result = originalGet.apply(this, arguments)
35
- if (!urlGetterChannel.hasSubscribers) return result
34
+ const context = { urlObject: this, result, property }
35
+ urlGetterChannel.publish(context)
36
36
 
37
- const context = { urlObject: this, result, property }
38
- urlGetterChannel.publish(context)
39
-
40
- return context.result
41
- }
42
- })
43
-
44
- Object.defineProperty(URLPrototype, property, newDescriptor)
45
- }
37
+ return context.result
38
+ }
39
+ })
46
40
  })
47
41
 
48
42
  shimmer.wrap(url, 'URL', (URL) => {
@@ -83,6 +77,4 @@ addHook({ name: names }, function (url) {
83
77
  }
84
78
  })
85
79
  }
86
-
87
- return url
88
80
  })
@@ -26,7 +26,7 @@ const knownTestsCh = channel('ci:vitest:known-tests')
26
26
  const isEarlyFlakeDetectionFaultyCh = channel('ci:vitest:is-early-flake-detection-faulty')
27
27
  const testManagementTestsCh = channel('ci:vitest:test-management-tests')
28
28
 
29
- const taskToAsync = new WeakMap()
29
+ const taskToCtx = new WeakMap()
30
30
  const taskToStatuses = new WeakMap()
31
31
  const newTasks = new WeakSet()
32
32
  const disabledTasks = new WeakSet()
@@ -455,7 +455,7 @@ addHook({
455
455
  // test start (only tests that are not marked as skip or todo)
456
456
  // `onBeforeTryTask` is run for every repetition and attempt of the test
457
457
  shimmer.wrap(VitestTestRunner.prototype, 'onBeforeTryTask', onBeforeTryTask => async function (task, retryInfo) {
458
- if (!testStartCh.hasSubscribers) {
458
+ if (!testPassCh.hasSubscribers && !testErrorCh.hasSubscribers && !testSkipCh.hasSubscribers) {
459
459
  return onBeforeTryTask.apply(this, arguments)
460
460
  }
461
461
  const testName = getTestName(task)
@@ -500,15 +500,14 @@ addHook({
500
500
 
501
501
  const promises = {}
502
502
  const shouldSetProbe = isDiEnabled && numAttempt === 1
503
- const asyncResource = taskToAsync.get(task)
503
+ const ctx = taskToCtx.get(task)
504
504
  const testError = task.result?.errors?.[0]
505
- if (asyncResource) {
506
- asyncResource.runInAsyncScope(() => {
507
- testErrorCh.publish({
508
- error: testError,
509
- shouldSetProbe,
510
- promises
511
- })
505
+ if (ctx) {
506
+ testErrorCh.publish({
507
+ error: testError,
508
+ shouldSetProbe,
509
+ promises,
510
+ ...ctx.currentStore
512
511
  })
513
512
  // We wait for the probe to be set
514
513
  if (promises.setProbePromise) {
@@ -528,17 +527,13 @@ addHook({
528
527
  // as long as it's not the _last_ iteration (which will be finished normally)
529
528
 
530
529
  // TODO: check test duration (not to repeat if it's too slow)
531
- const asyncResource = taskToAsync.get(task)
532
- if (asyncResource) {
530
+ const ctx = taskToCtx.get(task)
531
+ if (ctx) {
533
532
  if (lastExecutionStatus === 'fail') {
534
533
  const testError = task.result?.errors?.[0]
535
- asyncResource.runInAsyncScope(() => {
536
- testErrorCh.publish({ error: testError })
537
- })
534
+ testErrorCh.publish({ error: testError, ...ctx.currentStore })
538
535
  } else {
539
- asyncResource.runInAsyncScope(() => {
540
- testPassCh.publish({ task })
541
- })
536
+ testPassCh.publish({ task, ...ctx.currentStore })
542
537
  }
543
538
  if (shouldFlipStatus) {
544
539
  statuses.push(lastExecutionStatus)
@@ -555,16 +550,12 @@ addHook({
555
550
  statuses.push(lastExecutionStatus)
556
551
  }
557
552
 
558
- const asyncResource = taskToAsync.get(task)
553
+ const ctx = taskToCtx.get(task)
559
554
  if (lastExecutionStatus === 'fail') {
560
555
  const testError = task.result?.errors?.[0]
561
- asyncResource.runInAsyncScope(() => {
562
- testErrorCh.publish({ error: testError })
563
- })
556
+ testErrorCh.publish({ error: testError, ...ctx.currentStore })
564
557
  } else {
565
- asyncResource.runInAsyncScope(() => {
566
- testPassCh.publish({ task })
567
- })
558
+ testPassCh.publish({ task, ...ctx.currentStore })
568
559
  }
569
560
  }
570
561
 
@@ -573,31 +564,29 @@ addHook({
573
564
  !isRetryReasonAttemptToFix &&
574
565
  !isRetryReasonEfd
575
566
 
576
- const asyncResource = new AsyncResource('bound-anonymous-fn')
577
- taskToAsync.set(task, asyncResource)
567
+ const ctx = {
568
+ testName,
569
+ testSuiteAbsolutePath: task.file.filepath,
570
+ isRetry: numAttempt > 0 || numRepetition > 0,
571
+ isRetryReasonEfd,
572
+ isRetryReasonAttemptToFix: isRetryReasonAttemptToFix && numRepetition > 0,
573
+ isNew,
574
+ mightHitProbe: isDiEnabled && numAttempt > 0,
575
+ isAttemptToFix: attemptToFixTasks.has(task),
576
+ isDisabled: disabledTasks.has(task),
577
+ isQuarantined,
578
+ isRetryReasonAtr
579
+ }
580
+ taskToCtx.set(task, ctx)
578
581
 
579
- asyncResource.runInAsyncScope(() => {
580
- testStartCh.publish({
581
- testName,
582
- testSuiteAbsolutePath: task.file.filepath,
583
- isRetry: numAttempt > 0 || numRepetition > 0,
584
- isRetryReasonEfd,
585
- isRetryReasonAttemptToFix: isRetryReasonAttemptToFix && numRepetition > 0,
586
- isNew,
587
- mightHitProbe: isDiEnabled && numAttempt > 0,
588
- isAttemptToFix: attemptToFixTasks.has(task),
589
- isDisabled: disabledTasks.has(task),
590
- isQuarantined,
591
- isRetryReasonAtr
592
- })
593
- })
582
+ testStartCh.runStores(ctx, () => { })
594
583
  return onBeforeTryTask.apply(this, arguments)
595
584
  })
596
585
 
597
586
  // test finish (only passed tests)
598
587
  shimmer.wrap(VitestTestRunner.prototype, 'onAfterTryTask', onAfterTryTask =>
599
588
  async function (task, { retry: retryCount }) {
600
- if (!testFinishTimeCh.hasSubscribers) {
589
+ if (!testPassCh.hasSubscribers && !testErrorCh.hasSubscribers && !testSkipCh.hasSubscribers) {
601
590
  return onAfterTryTask.apply(this, arguments)
602
591
  }
603
592
  const result = await onAfterTryTask.apply(this, arguments)
@@ -605,7 +594,7 @@ addHook({
605
594
  const { testManagementAttemptToFixRetries } = getProvidedContext()
606
595
 
607
596
  const status = getVitestTestStatus(task, retryCount)
608
- const asyncResource = taskToAsync.get(task)
597
+ const ctx = taskToCtx.get(task)
609
598
 
610
599
  const { isDiEnabled } = getProvidedContext()
611
600
 
@@ -614,18 +603,25 @@ addHook({
614
603
  }
615
604
 
616
605
  let attemptToFixPassed = false
606
+ let attemptToFixFailed = false
617
607
  if (attemptToFixTasks.has(task)) {
618
608
  const statuses = taskToStatuses.get(task)
619
- if (statuses.length === testManagementAttemptToFixRetries && statuses.every(status => status === 'pass')) {
620
- attemptToFixPassed = true
609
+ if (statuses.length === testManagementAttemptToFixRetries) {
610
+ if (statuses.every(status => status === 'pass')) {
611
+ attemptToFixPassed = true
612
+ } else if (statuses.some(status => status === 'fail')) {
613
+ attemptToFixFailed = true
614
+ }
621
615
  }
622
616
  }
623
617
 
624
- if (asyncResource) {
618
+ if (ctx) {
625
619
  // We don't finish here because the test might fail in a later hook (afterEach)
626
- asyncResource.runInAsyncScope(() => {
627
- testFinishTimeCh.publish({ status, task, attemptToFixPassed })
628
- })
620
+ ctx.status = status
621
+ ctx.task = task
622
+ ctx.attemptToFixPassed = attemptToFixPassed
623
+ ctx.attemptToFixFailed = attemptToFixFailed
624
+ testFinishTimeCh.runStores(ctx, () => { })
629
625
  }
630
626
 
631
627
  return result
@@ -723,19 +719,14 @@ addHook({
723
719
  }, (vitestPackage, frameworkVersion) => {
724
720
  shimmer.wrap(vitestPackage, 'startTests', startTests => async function (testPaths) {
725
721
  let testSuiteError = null
726
- if (!testSuiteStartCh.hasSubscribers) {
722
+ if (!testSuiteFinishCh.hasSubscribers) {
727
723
  return startTests.apply(this, arguments)
728
724
  }
729
725
  // From >=3.0.1, the first arguments changes from a string to an object containing the filepath
730
726
  const testSuiteAbsolutePath = testPaths[0]?.filepath || testPaths[0]
731
727
 
732
- const testSuiteAsyncResource = new AsyncResource('bound-anonymous-fn')
733
- testSuiteAsyncResource.runInAsyncScope(() => {
734
- testSuiteStartCh.publish({
735
- testSuiteAbsolutePath,
736
- frameworkVersion
737
- })
738
- })
728
+ const testSuiteCtx = { testSuiteAbsolutePath, frameworkVersion }
729
+ testSuiteStartCh.runStores(testSuiteCtx, () => { })
739
730
  const startTestsResponse = await startTests.apply(this, arguments)
740
731
 
741
732
  let onFinish = null
@@ -747,7 +738,7 @@ addHook({
747
738
 
748
739
  // Only one test task per test, even if there are retries
749
740
  testTasks.forEach(task => {
750
- const testAsyncResource = taskToAsync.get(task)
741
+ const testCtx = taskToCtx.get(task)
751
742
  const { result } = task
752
743
  // We have to trick vitest into thinking that the test has passed
753
744
  // but we want to report it as failed if it did fail
@@ -763,10 +754,8 @@ addHook({
763
754
  isDisabled: disabledTasks.has(task)
764
755
  })
765
756
  } else if (state === 'pass' && !isSwitchedStatus) {
766
- if (testAsyncResource) {
767
- testAsyncResource.runInAsyncScope(() => {
768
- testPassCh.publish({ task })
769
- })
757
+ if (testCtx) {
758
+ testPassCh.publish({ task, ...testCtx.currentStore })
770
759
  }
771
760
  } else if (state === 'fail' || isSwitchedStatus) {
772
761
  let testError
@@ -776,18 +765,26 @@ addHook({
776
765
  }
777
766
 
778
767
  let hasFailedAllRetries = false
768
+ let attemptToFixFailed = false
779
769
  if (attemptToFixTasks.has(task)) {
780
770
  const statuses = taskToStatuses.get(task)
771
+ if (statuses.some(status => status === 'fail')) {
772
+ attemptToFixFailed = true
773
+ }
781
774
  if (statuses.every(status => status === 'fail')) {
782
775
  hasFailedAllRetries = true
783
776
  }
784
777
  }
785
778
 
786
- if (testAsyncResource) {
779
+ if (testCtx) {
787
780
  const isRetry = task.result?.retryCount > 0
788
781
  // `duration` is the duration of all the retries, so it can't be used if there are retries
789
- testAsyncResource.runInAsyncScope(() => {
790
- testErrorCh.publish({ duration: !isRetry ? duration : undefined, error: testError, hasFailedAllRetries })
782
+ testErrorCh.publish({
783
+ duration: !isRetry ? duration : undefined,
784
+ error: testError,
785
+ hasFailedAllRetries,
786
+ attemptToFixFailed,
787
+ ...testCtx.currentStore
791
788
  })
792
789
  }
793
790
  if (errors?.length) {
@@ -817,14 +814,11 @@ addHook({
817
814
  }
818
815
 
819
816
  if (testSuiteError) {
820
- testSuiteAsyncResource.runInAsyncScope(() => {
821
- testSuiteErrorCh.publish({ error: testSuiteError })
822
- })
817
+ testSuiteCtx.error = testSuiteError
818
+ testSuiteErrorCh.runStores(testSuiteCtx, () => { })
823
819
  }
824
820
 
825
- testSuiteAsyncResource.runInAsyncScope(() => {
826
- testSuiteFinishCh.publish({ status: testSuiteResult.state, onFinish })
827
- })
821
+ testSuiteFinishCh.publish({ status: testSuiteResult.state, onFinish, ...testSuiteCtx.currentStore })
828
822
 
829
823
  // TODO: fix too frequent flushes
830
824
  await onFinishPromise
@@ -0,0 +1,11 @@
1
+ 'use strict'
2
+
3
+ const KafkajsBatchConsumerPlugin = require('../../datadog-plugin-kafkajs/src/batch-consumer')
4
+
5
+ class ConfluentKafkaJsBatchConsumerPlugin extends KafkajsBatchConsumerPlugin {
6
+ static get id () {
7
+ return '@confluentinc/kafka-javascript'
8
+ }
9
+ }
10
+
11
+ module.exports = ConfluentKafkaJsBatchConsumerPlugin
@@ -0,0 +1,11 @@
1
+ 'use strict'
2
+
3
+ const KafkajsConsumerPlugin = require('../../datadog-plugin-kafkajs/src/consumer')
4
+
5
+ class ConfluentKafkaJsConsumerPlugin extends KafkajsConsumerPlugin {
6
+ static get id () {
7
+ return '@confluentinc/kafka-javascript'
8
+ }
9
+ }
10
+
11
+ module.exports = ConfluentKafkaJsConsumerPlugin