dd-trace 6.10.0 → 6.12.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 (173) hide show
  1. package/LICENSE-3rdparty.csv +5 -0
  2. package/README.md +0 -17
  3. package/index.d.ts +128 -6
  4. package/initialize.mjs +28 -17
  5. package/openfeature.js +2 -2
  6. package/package.json +27 -23
  7. package/packages/datadog-esbuild/index.js +0 -34
  8. package/packages/datadog-instrumentations/src/anthropic.js +210 -15
  9. package/packages/datadog-instrumentations/src/aws-sdk.js +9 -1
  10. package/packages/datadog-instrumentations/src/browser-bunyan.js +11 -0
  11. package/packages/datadog-instrumentations/src/bunyan.js +3 -16
  12. package/packages/datadog-instrumentations/src/cucumber.js +30 -15
  13. package/packages/datadog-instrumentations/src/cypress-config.js +182 -16
  14. package/packages/datadog-instrumentations/src/cypress-legacy-finalizers.js +30 -0
  15. package/packages/datadog-instrumentations/src/cypress.js +2 -0
  16. package/packages/datadog-instrumentations/src/fastify.js +2 -0
  17. package/packages/datadog-instrumentations/src/helpers/bunyan.js +22 -0
  18. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -1
  19. package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +9 -4
  20. package/packages/datadog-instrumentations/src/helpers/register.js +10 -8
  21. package/packages/datadog-instrumentations/src/jest.js +25 -17
  22. package/packages/datadog-instrumentations/src/mariadb.js +278 -21
  23. package/packages/datadog-instrumentations/src/mocha/main.js +539 -55
  24. package/packages/datadog-instrumentations/src/mocha/utils.js +135 -19
  25. package/packages/datadog-instrumentations/src/playwright-reporter.js +247 -0
  26. package/packages/datadog-instrumentations/src/playwright.js +182 -35
  27. package/packages/datadog-instrumentations/src/router.js +2 -0
  28. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +6 -8
  29. package/packages/datadog-instrumentations/src/vitest-main.js +11 -3
  30. package/packages/datadog-plugin-aws-sdk/src/base.js +1 -0
  31. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +163 -44
  32. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +2 -8
  33. package/packages/datadog-plugin-azure-functions/src/index.js +1 -1
  34. package/packages/datadog-plugin-browser-bunyan/src/index.js +9 -0
  35. package/packages/datadog-plugin-bunyan/src/index.js +5 -8
  36. package/packages/datadog-plugin-cucumber/src/index.js +11 -0
  37. package/packages/datadog-plugin-cypress/src/after-run.js +5 -1
  38. package/packages/datadog-plugin-cypress/src/after-spec.js +5 -1
  39. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +81 -29
  40. package/packages/datadog-plugin-cypress/src/finalization.js +76 -0
  41. package/packages/datadog-plugin-cypress/src/index.js +12 -6
  42. package/packages/datadog-plugin-cypress/src/plugin.js +16 -3
  43. package/packages/datadog-plugin-dd-trace-api/src/index.js +4 -0
  44. package/packages/datadog-plugin-electron/src/index.js +2 -0
  45. package/packages/datadog-plugin-fs/src/index.js +1 -1
  46. package/packages/datadog-plugin-jest/src/index.js +6 -0
  47. package/packages/datadog-plugin-mocha/src/index.js +56 -1
  48. package/packages/datadog-plugin-nats/src/index.js +1 -1
  49. package/packages/datadog-plugin-openai/src/tracing.js +41 -30
  50. package/packages/datadog-plugin-openai-agents/src/integration.js +1 -0
  51. package/packages/datadog-plugin-playwright/src/index.js +46 -13
  52. package/packages/datadog-plugin-vitest/src/index.js +6 -0
  53. package/packages/datadog-webpack/index.js +0 -11
  54. package/packages/dd-trace/index.js +0 -1
  55. package/packages/dd-trace/src/agent/info.js +6 -5
  56. package/packages/dd-trace/src/aiguard/client.js +100 -1
  57. package/packages/dd-trace/src/aiguard/errors.js +41 -0
  58. package/packages/dd-trace/src/aiguard/evaluation.js +357 -0
  59. package/packages/dd-trace/src/aiguard/integrations/anthropic.js +70 -0
  60. package/packages/dd-trace/src/aiguard/integrations/index.js +4 -0
  61. package/packages/dd-trace/src/aiguard/messages/anthropic.js +423 -0
  62. package/packages/dd-trace/src/aiguard/messages/openai.js +1 -24
  63. package/packages/dd-trace/src/aiguard/messages/utils.js +28 -0
  64. package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +1 -11
  65. package/packages/dd-trace/src/aiguard/sdk.js +22 -278
  66. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +9 -12
  67. package/packages/dd-trace/src/appsec/reporter.js +2 -0
  68. package/packages/dd-trace/src/appsec/telemetry/index.js +13 -0
  69. package/packages/dd-trace/src/appsec/telemetry/rasp.js +16 -0
  70. package/packages/dd-trace/src/appsec/telemetry/waf.js +24 -0
  71. package/packages/dd-trace/src/carrier.js +107 -48
  72. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +1 -0
  73. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +5 -5
  74. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +29 -3
  75. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +27 -9
  76. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +21 -5
  77. package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +164 -0
  78. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +26 -8
  79. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +365 -32
  80. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +6 -4
  81. package/packages/dd-trace/src/ci-visibility/exporters/request.js +201 -0
  82. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +5 -3
  83. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +9 -5
  84. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +7 -4
  85. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +4 -2
  86. package/packages/dd-trace/src/ci-visibility/requests/rate-limit.js +39 -0
  87. package/packages/dd-trace/src/ci-visibility/requests/request.js +3 -6
  88. package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +4 -2
  89. package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +10 -4
  90. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +5 -5
  91. package/packages/dd-trace/src/config/generated-config-types.d.ts +4 -0
  92. package/packages/dd-trace/src/config/index.js +13 -2
  93. package/packages/dd-trace/src/config/major-overrides.js +7 -0
  94. package/packages/dd-trace/src/config/supported-configurations.json +14 -0
  95. package/packages/dd-trace/src/datastreams/index.js +2 -0
  96. package/packages/dd-trace/src/datastreams/manager.js +9 -2
  97. package/packages/dd-trace/src/datastreams/processor.js +15 -13
  98. package/packages/dd-trace/src/datastreams/size.js +5 -0
  99. package/packages/dd-trace/src/debugger/constants.js +1 -0
  100. package/packages/dd-trace/src/debugger/devtools_client/condition.js +1 -1
  101. package/packages/dd-trace/src/debugger/devtools_client/index.js +5 -11
  102. package/packages/dd-trace/src/debugger/index.js +4 -2
  103. package/packages/dd-trace/src/debugger/inspect-segment.js +154 -0
  104. package/packages/dd-trace/src/evp_proxy/constants.js +8 -0
  105. package/packages/dd-trace/src/evp_proxy/direct.js +64 -0
  106. package/packages/dd-trace/src/evp_proxy/discovery.js +133 -0
  107. package/packages/dd-trace/src/evp_proxy/path.js +22 -0
  108. package/packages/dd-trace/src/exporters/agent/writer.js +49 -9
  109. package/packages/dd-trace/src/exporters/common/buffering-exporter.js +6 -4
  110. package/packages/dd-trace/src/exporters/common/request.js +5 -2
  111. package/packages/dd-trace/src/exporters/common/writer.js +11 -6
  112. package/packages/dd-trace/src/llmobs/experiments/client.js +167 -5
  113. package/packages/dd-trace/src/llmobs/experiments/dataset.js +484 -92
  114. package/packages/dd-trace/src/llmobs/experiments/experiment.js +302 -29
  115. package/packages/dd-trace/src/llmobs/experiments/index.js +75 -64
  116. package/packages/dd-trace/src/llmobs/experiments/noop.js +135 -7
  117. package/packages/dd-trace/src/llmobs/experiments/result.js +1 -0
  118. package/packages/dd-trace/src/llmobs/experiments/util.js +101 -9
  119. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +12 -3
  120. package/packages/dd-trace/src/llmobs/plugins/base.js +4 -2
  121. package/packages/dd-trace/src/noop/proxy.js +3 -4
  122. package/packages/dd-trace/src/openfeature/constants/constants.js +0 -18
  123. package/packages/dd-trace/src/openfeature/flagging_provider.js +18 -2
  124. package/packages/dd-trace/src/openfeature/index.js +7 -4
  125. package/packages/dd-trace/src/openfeature/remote_config.js +1 -1
  126. package/packages/dd-trace/src/openfeature/writers/base.js +153 -11
  127. package/packages/dd-trace/src/openfeature/writers/exposures.js +62 -14
  128. package/packages/dd-trace/src/openfeature/writers/util.js +97 -15
  129. package/packages/dd-trace/src/opentelemetry/context_manager.js +7 -7
  130. package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +2 -1
  131. package/packages/dd-trace/src/opentelemetry/logs/logger.js +67 -11
  132. package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +14 -3
  133. package/packages/dd-trace/src/opentelemetry/metrics/index.js +25 -12
  134. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +2 -5
  135. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +96 -43
  136. package/packages/dd-trace/src/opentracing/propagation/text_map.js +22 -13
  137. package/packages/dd-trace/src/opentracing/tracer.js +5 -2
  138. package/packages/dd-trace/src/otel-thread-ctx.js +387 -0
  139. package/packages/dd-trace/src/plugin_manager.js +3 -3
  140. package/packages/dd-trace/src/plugins/ci_plugin.js +6 -1
  141. package/packages/dd-trace/src/plugins/index.js +1 -0
  142. package/packages/dd-trace/src/plugins/util/jest.js +4 -3
  143. package/packages/dd-trace/src/plugins/util/test.js +9 -9
  144. package/packages/dd-trace/src/plugins/util/web.js +3 -0
  145. package/packages/dd-trace/src/profiling/profiler.js +1 -1
  146. package/packages/dd-trace/src/profiling/profilers/space.js +1 -3
  147. package/packages/dd-trace/src/profiling/profilers/wall.js +37 -90
  148. package/packages/dd-trace/src/profiling/webspan-utils.js +45 -0
  149. package/packages/dd-trace/src/proxy.js +38 -29
  150. package/packages/dd-trace/src/ritm.js +9 -2
  151. package/packages/dd-trace/src/serverless.js +37 -0
  152. package/packages/dd-trace/src/span_format.js +1 -0
  153. package/packages/dd-trace/src/span_stats.js +30 -3
  154. package/packages/dd-trace/src/storage-channels.js +86 -0
  155. package/packages/dd-trace/src/telemetry/metrics.js +62 -8
  156. package/packages/dd-trace/src/telemetry/send-data.js +1 -0
  157. package/packages/dd-trace/src/tracer.js +9 -2
  158. package/packages/dd-trace/src/tracer_metadata.js +14 -1
  159. package/packages/dd-trace/src/web-tags-cache.js +132 -0
  160. package/vendor/dist/@datadog/openfeature-node-server/LICENSE +201 -0
  161. package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -0
  162. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  163. package/vendor/dist/https-proxy-agent/LICENSE +22 -0
  164. package/vendor/dist/https-proxy-agent/index.js +4 -0
  165. package/vendor/dist/protobufjs/index.js +1 -1
  166. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  167. package/vendor/dist/proxy-from-env/LICENSE +20 -0
  168. package/vendor/dist/proxy-from-env/index.js +1 -0
  169. package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +0 -65
  170. package/packages/datadog-webpack/src/optional-peer-loader.js +0 -17
  171. package/packages/dd-trace/src/feature-registry.js +0 -29
  172. package/packages/dd-trace/src/openfeature/register.js +0 -35
  173. package/packages/dd-trace/src/openfeature/require-provider.js +0 -18
@@ -7,9 +7,18 @@ const { pathToFileURL } = require('url')
7
7
 
8
8
  const log = require('../../dd-trace/src/log')
9
9
  const { getSegment } = require('../../dd-trace/src/util')
10
+ const {
11
+ finalizeAfterUserHandlers,
12
+ manualPluginOwner,
13
+ runUserHandler,
14
+ } = require('../../datadog-plugin-cypress/src/finalization')
10
15
  const { channel } = require('./helpers/instrument')
11
16
 
12
17
  const DD_CONFIG_WRAPPED = Symbol.for('dd-trace.cypress.config.wrapped')
18
+ const DD_CYPRESS_AFTER_SPEC_HANDLER = Symbol.for('dd-trace.cypress.after-spec.handler')
19
+ const DD_CYPRESS_AFTER_RUN_HANDLER = Symbol.for('dd-trace.cypress.after-run.handler')
20
+ const DD_CYPRESS_TASK_HANDLER = Symbol.for('dd-trace.cypress.task.handler')
21
+ const DD_CYPRESS_NOOP_TASK_HANDLER = Symbol.for('dd-trace.cypress.noop-task.handler')
13
22
  const BROWSER_INSTRUMENTATION_NOT_INSTALLED =
14
23
  'Browser-side Cypress Test Optimization instrumentation was not installed.'
15
24
  const CONFIG_INSTRUMENTATION_NOT_INSTALLED =
@@ -184,6 +193,47 @@ function isDatadogTaskRegistration (handler) {
184
193
  typeof handler['dd:addTags'] === 'function'
185
194
  }
186
195
 
196
+ /**
197
+ * @param {unknown} handler Cypress after:run handler
198
+ * @returns {boolean} whether this is the handler registered by the manual Datadog plugin
199
+ */
200
+ function isDatadogAfterRunHandler (handler) {
201
+ return typeof handler === 'function' && handler[DD_CYPRESS_AFTER_RUN_HANDLER] === true
202
+ }
203
+
204
+ /**
205
+ * @param {unknown} handler Cypress after:spec handler
206
+ * @returns {boolean} whether this is the handler registered by the manual Datadog plugin
207
+ */
208
+ function isDatadogAfterSpecHandler (handler) {
209
+ return typeof handler === 'function' && handler[DD_CYPRESS_AFTER_SPEC_HANDLER] === true
210
+ }
211
+
212
+ /**
213
+ * @param {object} manualPlugin manual plugin registration state
214
+ * @returns {boolean} whether the manual plugin supports error-aware finalization
215
+ */
216
+ function supportsErrorAwareFinalization (manualPlugin) {
217
+ return isDatadogAfterSpecHandler(manualPlugin.afterSpecHandler) &&
218
+ isDatadogAfterRunHandler(manualPlugin.afterRunHandler)
219
+ }
220
+
221
+ /**
222
+ * @param {unknown} handler Cypress task handler map
223
+ * @returns {boolean} whether the current manual Datadog plugin owns the task map
224
+ */
225
+ function isCurrentDatadogTaskRegistration (handler) {
226
+ return !!handler && handler[DD_CYPRESS_TASK_HANDLER] === manualPluginOwner
227
+ }
228
+
229
+ /**
230
+ * @param {unknown} handler Cypress task handler map
231
+ * @returns {boolean} whether the manual plugin registered only fallback tasks
232
+ */
233
+ function isDatadogNoopTaskRegistration (handler) {
234
+ return !!handler && handler[DD_CYPRESS_NOOP_TASK_HANDLER] === true
235
+ }
236
+
187
237
  /**
188
238
  * @param {unknown} value
189
239
  * @returns {boolean}
@@ -393,15 +443,61 @@ function registerManualAfterScreenshotHandlers (on, handlers, datadogHandler) {
393
443
  *
394
444
  * @param {Function} on Cypress event registration function
395
445
  * @param {Function[]} handlers collected after:spec handlers
446
+ * @param {Function} [datadogHandler] manual Datadog after:spec handler
447
+ * @param {Function} [cleanup] removes generated support files after an error
396
448
  * @returns {void}
397
449
  */
398
- function registerAfterSpecHandlers (on, handlers) {
399
- if (handlers.length === 0) return
450
+ function registerAfterSpecHandlers (on, handlers, datadogHandler, cleanup) {
451
+ const userHandlers = datadogHandler
452
+ ? handlers.filter(handler => handler !== datadogHandler)
453
+ : handlers
400
454
 
401
- on('after:spec', (spec, results) => handlers.reduce(
402
- (chain, handler) => chain.then(() => handler(spec, results)),
403
- Promise.resolve()
404
- ))
455
+ if (userHandlers.length === 0) {
456
+ if (datadogHandler) {
457
+ if (cleanup) {
458
+ on('after:spec', (...args) => Promise.resolve().then(() => datadogHandler(...args)).catch((error) => {
459
+ cleanup()
460
+ throw error
461
+ }))
462
+ } else {
463
+ on('after:spec', datadogHandler)
464
+ }
465
+ }
466
+ return
467
+ }
468
+
469
+ on('after:spec', (spec, results) => {
470
+ const callHandler = datadogHandler
471
+ ? handler => runUserHandler(() => handler(spec, results))
472
+ : handler => handler(spec, results)
473
+ const chain = userHandlers.reduce(
474
+ (promise, handler) => promise.then(() => callHandler(handler)),
475
+ Promise.resolve()
476
+ )
477
+ if (!datadogHandler) return chain
478
+ const finalization = finalizeAfterUserHandlers(chain, (...args) => datadogHandler(spec, results, ...args))
479
+ if (!cleanup) return finalization
480
+ return finalization.catch((error) => {
481
+ cleanup()
482
+ throw error
483
+ })
484
+ })
485
+ }
486
+
487
+ /**
488
+ * Enables the Cypress lifecycle events needed to finish Test Optimization
489
+ * sessions in interactive mode.
490
+ *
491
+ * @param {object} config Cypress resolved config object
492
+ * @returns {void}
493
+ */
494
+ function enableInteractiveRunEvents (config) {
495
+ if (config.isInteractive && config.experimentalInteractiveRunEvents !== true) {
496
+ config.experimentalInteractiveRunEvents = true
497
+ log.warn(
498
+ 'Datadog enabled Cypress experimentalInteractiveRunEvents so Test Optimization can finish the test session.'
499
+ )
500
+ }
405
501
  }
406
502
 
407
503
  /**
@@ -412,6 +508,7 @@ function registerAfterSpecHandlers (on, handlers) {
412
508
  *
413
509
  * @param {Function} on Cypress event registration function
414
510
  * @param {object} config Cypress resolved config object
511
+ * @param {Function[]} userBeforeRunHandlers user's before:run handlers collected from wrappedOn
415
512
  * @param {Function[]} userAfterSpecHandlers user's after:spec handlers collected from wrappedOn
416
513
  * @param {Function[]} userAfterRunHandlers user's after:run handlers collected from wrappedOn
417
514
  * @param {Function[]} userAfterScreenshotHandlers user's after:screenshot handlers collected from wrappedOn
@@ -421,6 +518,7 @@ function registerAfterSpecHandlers (on, handlers) {
421
518
  function registerDdTraceHooks (
422
519
  on,
423
520
  config,
521
+ userBeforeRunHandlers,
424
522
  userAfterSpecHandlers,
425
523
  userAfterRunHandlers,
426
524
  userAfterScreenshotHandlers,
@@ -433,13 +531,23 @@ function registerDdTraceHooks (
433
531
  if (generatedSupportFiles) cleanupGeneratedFiles(generatedSupportFiles)
434
532
  }
435
533
 
436
- const registerAfterRunWithCleanup = () => {
534
+ const registerAfterRunWithCleanup = (datadogHandler) => {
535
+ const handlers = datadogHandler
536
+ ? userAfterRunHandlers.filter(handler => handler !== datadogHandler)
537
+ : userAfterRunHandlers
538
+
437
539
  on('after:run', (results) => {
438
- const chain = userAfterRunHandlers.reduce(
439
- (p, h) => p.then(() => h(results)),
540
+ const callHandler = datadogHandler
541
+ ? handler => runUserHandler(() => handler(results))
542
+ : handler => handler(results)
543
+ const chain = handlers.reduce(
544
+ (promise, handler) => promise.then(() => callHandler(handler)),
440
545
  Promise.resolve()
441
546
  )
442
- return chain.finally(cleanupWrapper)
547
+ if (!datadogHandler) return chain.finally(cleanupWrapper)
548
+
549
+ return finalizeAfterUserHandlers(chain, (...args) => datadogHandler(results, ...args))
550
+ .finally(cleanupWrapper)
443
551
  })
444
552
  }
445
553
 
@@ -450,13 +558,36 @@ function registerDdTraceHooks (
450
558
  on('task', noopTask)
451
559
  }
452
560
 
453
- if (manualPlugin.detected) {
454
- registerAfterSpecHandlers(on, userAfterSpecHandlers)
561
+ if (manualPlugin.detected &&
562
+ (manualPlugin.isNoop ||
563
+ manualPlugin.ownsCurrentPlugin ||
564
+ supportsErrorAwareFinalization(manualPlugin) ||
565
+ !setupNodeEventsCh.hasSubscribers)) {
566
+ if (manualPlugin.afterRunHandler) {
567
+ enableInteractiveRunEvents(config)
568
+ if (manualPlugin.initialConfig !== config) {
569
+ manualPlugin.initialConfig.experimentalInteractiveRunEvents = config.experimentalInteractiveRunEvents
570
+ }
571
+ }
572
+ for (const handler of userBeforeRunHandlers) on('before:run', handler)
573
+ registerAfterSpecHandlers(on, userAfterSpecHandlers, manualPlugin.afterSpecHandler, cleanupWrapper)
455
574
  registerManualAfterScreenshotHandlers(on, userAfterScreenshotHandlers, manualPlugin.afterScreenshotHandler)
456
- registerAfterRunWithCleanup()
575
+ registerAfterRunWithCleanup(manualPlugin.afterRunHandler)
576
+ on('task', manualPlugin.taskHandler)
457
577
  return config
458
578
  }
459
579
 
580
+ if (manualPlugin.detected) {
581
+ userBeforeRunHandlers = userBeforeRunHandlers.filter(handler => handler !== manualPlugin.beforeRunHandler)
582
+ userAfterSpecHandlers = userAfterSpecHandlers.filter(handler => handler !== manualPlugin.afterSpecHandler)
583
+ userAfterRunHandlers = userAfterRunHandlers.filter(handler => handler !== manualPlugin.afterRunHandler)
584
+ userAfterScreenshotHandlers = userAfterScreenshotHandlers.filter(
585
+ handler => handler !== manualPlugin.afterScreenshotHandler
586
+ )
587
+ }
588
+
589
+ for (const handler of userBeforeRunHandlers) on('before:run', handler)
590
+
460
591
  if (!setupNodeEventsCh.hasSubscribers) {
461
592
  registerNoopHandlers()
462
593
  return config
@@ -483,6 +614,7 @@ function registerDdTraceHooks (
483
614
  return config
484
615
  }
485
616
 
617
+ enableInteractiveRunEvents(config)
486
618
  return payload.configPromise || config
487
619
  }
488
620
 
@@ -492,28 +624,60 @@ function registerDdTraceHooks (
492
624
  */
493
625
  function wrapSetupNodeEvents (originalSetupNodeEvents) {
494
626
  return function ddSetupNodeEvents (on, config) {
627
+ const userBeforeRunHandlers = []
495
628
  const userAfterSpecHandlers = []
496
629
  const userAfterRunHandlers = []
497
630
  const userAfterScreenshotHandlers = []
498
631
  const manualPlugin = {
499
632
  detected: false,
633
+ initialConfig: config,
634
+ beforeRunHandler: undefined,
635
+ afterSpecHandler: undefined,
636
+ afterRunHandler: undefined,
500
637
  afterScreenshotHandler: undefined,
638
+ taskHandler: undefined,
639
+ ownsCurrentPlugin: false,
640
+ isNoop: false,
501
641
  }
642
+ const recentRegistrations = []
502
643
 
503
644
  const wrappedOn = (event, handler) => {
504
- if (event === 'after:spec') {
645
+ if (event === 'before:run') {
646
+ userBeforeRunHandlers.push(handler)
647
+ } else if (event === 'after:spec') {
505
648
  userAfterSpecHandlers.push(handler)
649
+ if (isDatadogAfterSpecHandler(handler)) manualPlugin.afterSpecHandler = handler
506
650
  } else if (event === 'after:run') {
507
651
  userAfterRunHandlers.push(handler)
652
+ if (isDatadogAfterRunHandler(handler)) manualPlugin.afterRunHandler = handler
508
653
  } else if (event === 'after:screenshot') {
509
654
  userAfterScreenshotHandlers.push(handler)
510
655
  } else {
511
656
  if (event === 'task' && isDatadogTaskRegistration(handler)) {
512
657
  manualPlugin.detected = true
513
- manualPlugin.afterScreenshotHandler = userAfterScreenshotHandlers.at(-1)
658
+ manualPlugin.ownsCurrentPlugin = isCurrentDatadogTaskRegistration(handler)
659
+ manualPlugin.isNoop = isDatadogNoopTaskRegistration(handler)
660
+ const afterRun = recentRegistrations.at(-1)
661
+ const afterSpec = recentRegistrations.at(-2)
662
+ const possibleAfterScreenshot = recentRegistrations.at(-3)
663
+ const hasAfterScreenshot = possibleAfterScreenshot?.event === 'after:screenshot'
664
+ const beforeRun = hasAfterScreenshot ? recentRegistrations.at(-4) : possibleAfterScreenshot
665
+ if (!manualPlugin.isNoop &&
666
+ beforeRun?.event === 'before:run' &&
667
+ afterSpec?.event === 'after:spec' &&
668
+ afterRun?.event === 'after:run') {
669
+ manualPlugin.beforeRunHandler ||= beforeRun.handler
670
+ manualPlugin.afterSpecHandler ||= afterSpec.handler
671
+ manualPlugin.afterRunHandler ||= afterRun.handler
672
+ if (hasAfterScreenshot) manualPlugin.afterScreenshotHandler ||= possibleAfterScreenshot.handler
673
+ }
674
+ manualPlugin.taskHandler = handler
675
+ } else {
676
+ on(event, handler)
514
677
  }
515
- on(event, handler)
516
678
  }
679
+ recentRegistrations.push({ event, handler })
680
+ if (recentRegistrations.length > 4) recentRegistrations.shift()
517
681
  }
518
682
 
519
683
  const maybePromise = originalSetupNodeEvents
@@ -525,6 +689,7 @@ function wrapSetupNodeEvents (originalSetupNodeEvents) {
525
689
  return registerDdTraceHooks(
526
690
  on,
527
691
  mergeReturnedConfig(config, result),
692
+ userBeforeRunHandlers,
528
693
  userAfterSpecHandlers,
529
694
  userAfterRunHandlers,
530
695
  userAfterScreenshotHandlers,
@@ -536,6 +701,7 @@ function wrapSetupNodeEvents (originalSetupNodeEvents) {
536
701
  return registerDdTraceHooks(
537
702
  on,
538
703
  mergeReturnedConfig(config, maybePromise),
704
+ userBeforeRunHandlers,
539
705
  userAfterSpecHandlers,
540
706
  userAfterRunHandlers,
541
707
  userAfterScreenshotHandlers,
@@ -0,0 +1,30 @@
1
+ 'use strict'
2
+
3
+ const { shouldDeferLegacyFinalization } = require('../../datadog-plugin-cypress/src/finalization')
4
+ const Hook = require('./helpers/hook')
5
+
6
+ const DD_CYPRESS_LEGACY_FINALIZER_WRAPPED = Symbol.for('dd-trace.cypress.legacy-finalizer.wrapped')
7
+ const LEGACY_FINALIZER_MODULES = new Set([
8
+ 'dd-trace/packages/datadog-plugin-cypress/src/after-run.js',
9
+ 'dd-trace/packages/datadog-plugin-cypress/src/after-spec.js',
10
+ ])
11
+
12
+ /**
13
+ * @param {Function} finalizer standalone Datadog Cypress finalizer
14
+ * @returns {Function} context-aware finalizer
15
+ */
16
+ function wrapLegacyFinalizer (finalizer) {
17
+ if (finalizer[DD_CYPRESS_LEGACY_FINALIZER_WRAPPED]) return finalizer
18
+
19
+ function wrappedLegacyFinalizer () {
20
+ if (shouldDeferLegacyFinalization()) return
21
+ return finalizer.apply(this, arguments)
22
+ }
23
+ wrappedLegacyFinalizer[DD_CYPRESS_LEGACY_FINALIZER_WRAPPED] = true
24
+ return wrappedLegacyFinalizer
25
+ }
26
+
27
+ Hook(['dd-trace'], { internals: true }, (moduleExports, moduleName) => {
28
+ if (!LEGACY_FINALIZER_MODULES.has(moduleName.replaceAll('\\', '/'))) return moduleExports
29
+ return wrapLegacyFinalizer(moduleExports)
30
+ })
@@ -8,6 +8,8 @@ const {
8
8
  wrapConfig,
9
9
  } = require('./cypress-config')
10
10
 
11
+ require('./cypress-legacy-finalizers')
12
+
11
13
  const MINIMUM_CYPRESS_VERSION = DD_MAJOR >= 6 ? '>=12.0.0' : '>=10.2.0'
12
14
 
13
15
  // Wrap defineConfig() so configs are instrumented when loaded in Cypress's
@@ -146,6 +146,7 @@ function wrapHookDone (ctx, request, reply, req, name, doneCallback) {
146
146
  ctx.error = error
147
147
  publishError(ctx)
148
148
 
149
+ // eslint-disable-next-line no-restricted-syntax -- arbitrary cookie names; publishing {} sets a WAF address
149
150
  const hasCookies = request.cookies && Object.keys(request.cookies).length > 0
150
151
 
151
152
  if (cookieParserReadCh.hasSubscribers && hasCookies && !cookiesPublished.has(req)) {
@@ -193,6 +194,7 @@ function preHandler (request, reply, done) {
193
194
  const res = getRes(reply)
194
195
  const ctx = { req, res }
195
196
 
197
+ // eslint-disable-next-line no-restricted-syntax -- arbitrary body keys; publishing {} sets a WAF address
196
198
  const hasBody = request.body && Object.keys(request.body).length > 0
197
199
 
198
200
  // For multipart/form-data, the body is not available until after preValidation hook
@@ -0,0 +1,22 @@
1
+ 'use strict'
2
+
3
+ const shimmer = require('../../../datadog-shimmer')
4
+ const { channel } = require('./instrument')
5
+
6
+ /**
7
+ * @param {{ prototype: object }} Logger
8
+ * @param {string} id
9
+ */
10
+ module.exports = function wrapLogger (Logger, id) {
11
+ const logCh = channel(`apm:${id}:log`)
12
+ shimmer.wrap(Logger.prototype, '_emit', emit => {
13
+ return function wrappedEmit (rec) {
14
+ if (logCh.hasSubscribers) {
15
+ const payload = { message: rec }
16
+ logCh.publish(payload)
17
+ arguments[0] = payload.message
18
+ }
19
+ return emit.apply(this, arguments)
20
+ }
21
+ })
22
+ }
@@ -70,6 +70,7 @@ module.exports = {
70
70
  'aws-sdk': () => require('../aws-sdk'),
71
71
  bluebird: () => require('../bluebird'),
72
72
  'body-parser': () => require('../body-parser'),
73
+ 'browser-bunyan': () => require('../browser-bunyan'),
73
74
  bullmq: () => require('../bullmq'),
74
75
  bunyan: () => require('../bunyan'),
75
76
  'cassandra-driver': () => require('../cassandra-driver'),
@@ -110,7 +111,7 @@ module.exports = {
110
111
  'light-my-request': () => require('../light-my-request'),
111
112
  'limitd-client': () => require('../limitd-client'),
112
113
  lodash: () => require('../lodash'),
113
- mariadb: () => require('../mariadb'),
114
+ mariadb: { esmFirst: true, fn: () => require('../mariadb') },
114
115
  memcached: () => require('../memcached'),
115
116
  mercurius: () => require('../mercurius'),
116
117
  'microgateway-core': () => require('../microgateway-core'),
@@ -518,10 +518,11 @@ function runPoolQueryConnectionCallback (
518
518
  * @param {unknown} error
519
519
  * @param {Record<string, unknown>} acquireCtx
520
520
  * @param {AcquireErrorChannels} channels
521
+ * @param {number} [poolWaitTime]
521
522
  */
522
- function reportPoolAcquireError (start, error, acquireCtx, channels) {
523
+ function reportPoolAcquireError (start, error, acquireCtx, channels, poolWaitTime) {
523
524
  acquireCtx.error = error
524
- acquireCtx.poolWaitTime = acquireWait(start)
525
+ acquireCtx.poolWaitTime = poolWaitTime ?? acquireWait(start)
525
526
  if (start !== undefined) acquireCtx.startTime = performance.timeOrigin + start
526
527
  channels.acquireStartCh.publish(acquireCtx)
527
528
  channels.acquireFinishCh.publish(acquireCtx)
@@ -536,11 +537,14 @@ function reportPoolAcquireError (start, error, acquireCtx, channels) {
536
537
  * @param {Function} callback
537
538
  * @param {unknown} thisArg
538
539
  * @param {ArgumentsLike} args
540
+ * @param {number} [poolWaitTime]
539
541
  * @returns {unknown}
540
542
  */
541
- function runPoolAcquireError (start, error, acquireCtx, channels, connectionCtx, callback, thisArg, args) {
543
+ function runPoolAcquireError (
544
+ start, error, acquireCtx, channels, connectionCtx, callback, thisArg, args, poolWaitTime
545
+ ) {
542
546
  return channels.connectionFinishCh.runStores(connectionCtx, () => {
543
- reportPoolAcquireError(start, error, acquireCtx, channels)
547
+ reportPoolAcquireError(start, error, acquireCtx, channels, poolWaitTime)
544
548
  return callback.apply(thisArg, args)
545
549
  })
546
550
  }
@@ -613,6 +617,7 @@ function takeClusterAcquire (key) {
613
617
  }
614
618
 
615
619
  module.exports = {
620
+ acquireWait,
616
621
  clearPoolWaitTime,
617
622
  dispatchesAcquireSynchronously,
618
623
  isPoolQueryAcquire,
@@ -112,14 +112,6 @@ for (const name of names) {
112
112
  }
113
113
 
114
114
  for (const { file, versions, hook, filePattern, patchDefault } of instrumentations[name]) {
115
- if (isIitm && patchDefault === !!moduleExports.default) {
116
- if (patchDefault) {
117
- moduleExports = moduleExports.default
118
- } else {
119
- return moduleExports
120
- }
121
- }
122
-
123
115
  const fullFilename = filename(name, file)
124
116
 
125
117
  let matchesFile = moduleName === fullFilename
@@ -134,6 +126,16 @@ for (const name of names) {
134
126
  }
135
127
 
136
128
  if (matchesFile && matchVersion(moduleVersion, versions)) {
129
+ // IITM invokes this callback for every module in the package. Only unwrap the namespace after its file and
130
+ // version match, otherwise a default export from an unrelated internal module can replace that module.
131
+ if (isIitm && patchDefault === !!moduleExports.default) {
132
+ if (patchDefault) {
133
+ moduleExports = moduleExports.default
134
+ } else {
135
+ return moduleExports
136
+ }
137
+ }
138
+
137
139
  // Do not log in case of an error to prevent duplicate telemetry for the same integration version.
138
140
  instrumentedIntegrationsSuccess.set(`${name}@${moduleVersion}`, true)
139
141
  try {
@@ -97,7 +97,9 @@ const itrSkippedSuitesCh = channel('ci:jest:itr:skipped-suites')
97
97
  const CHILD_MESSAGE_CALL = 1
98
98
 
99
99
  // Maximum time we'll wait for the tracer to flush
100
- const FLUSH_TIMEOUT = 10_000
100
+ // The exporter has a 10-second bounded final-flush deadline. Leave enough time
101
+ // for its completion callback before Jest's --forceExit fallback takes over.
102
+ const FLUSH_TIMEOUT = 12_000
101
103
  const JEST_SESSION_STATE = Symbol.for('dd-trace:jest:session')
102
104
  const JEST_BAIL_REPORTER_PATH = require.resolve('./jest/bail-reporter')
103
105
  const DD_JEST_HANDLE_TEST_EVENT_WRAPPED = Symbol('dd-trace:jest:handle-test-event-wrapped')
@@ -110,7 +112,7 @@ const jestSessionState = (globalThis[JEST_SESSION_STATE] ||= {})
110
112
  const RETRY_TIMES = Symbol.for('RETRY_TIMES')
111
113
 
112
114
  let skippableSuites = []
113
- let skippableSuitesCoverage = {}
115
+ let skippableSuitesCoverage
114
116
  let skippedSuitesCoverage = {}
115
117
  let knownTests = {}
116
118
  let isCodeCoverageEnabled = false
@@ -131,7 +133,7 @@ let isTestManagementTestsEnabled = false
131
133
  let testManagementTests = {}
132
134
  let testManagementAttemptToFixRetries = 0
133
135
  let isImpactedTestsEnabled = false
134
- let modifiedFiles = {}
136
+ let modifiedFiles
135
137
  let repositoryRoot
136
138
  let lastCoverageMap
137
139
  let lastCoverageMapRootDir
@@ -688,8 +690,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
688
690
 
689
691
  if (this.isImpactedTestsEnabled) {
690
692
  try {
691
- const hasImpactedTests = Object.keys(modifiedFiles).length > 0
692
- this.modifiedFiles = hasImpactedTests ? modifiedFiles : this.testEnvironmentOptions._ddModifiedFiles
693
+ this.modifiedFiles = modifiedFiles ?? this.testEnvironmentOptions._ddModifiedFiles
693
694
  } catch (e) {
694
695
  log.error('Error parsing impacted tests', e)
695
696
  this.isImpactedTestsEnabled = false
@@ -2489,12 +2490,6 @@ function getRepositoryRootFromTest (test, fallbackRootDir) {
2489
2490
  return getRepositoryRootFromConfig(test?.context?.config, fallbackRootDir)
2490
2491
  }
2491
2492
 
2492
- function hasSkippableSuitesCoverage () {
2493
- return skippableSuitesCoverage &&
2494
- typeof skippableSuitesCoverage === 'object' &&
2495
- Object.keys(skippableSuitesCoverage).length > 0
2496
- }
2497
-
2498
2493
  function shouldCollectJestCoverageForTia () {
2499
2494
  return shouldReportJestSuiteCoverageForTia() ||
2500
2495
  (isJestCoverageBackfillSupported && isItrEnabled && isCoverageReportUploadEnabled)
@@ -2605,7 +2600,7 @@ function resetLibraryConfiguration () {
2605
2600
  testManagementTests = {}
2606
2601
  testManagementAttemptToFixRetries = 0
2607
2602
  isImpactedTestsEnabled = false
2608
- modifiedFiles = {}
2603
+ modifiedFiles = undefined
2609
2604
  repositoryRoot = undefined
2610
2605
  }
2611
2606
 
@@ -2627,13 +2622,14 @@ function applySuiteSkipping (originalTests, rootDir, frameworkVersion) {
2627
2622
 
2628
2623
  isSuitesSkipped ||= jestSuitesToRun.suitesToRun.length !== originalTests.length
2629
2624
  numSkippedSuites += jestSuitesToRun.skippedSuites.length
2630
- skippedSuitesCoverage = isSuitesSkipped && isTiaCoverageBackfillEnabled() && hasSkippableSuitesCoverage()
2625
+ const hasSkippableSuitesCoverage = skippableSuitesCoverage !== undefined
2626
+ skippedSuitesCoverage = isSuitesSkipped && isTiaCoverageBackfillEnabled() && hasSkippableSuitesCoverage
2631
2627
  ? skippableSuitesCoverage
2632
2628
  : {}
2633
2629
  coverageBackfillContexts = isSuitesSkipped && isTiaCoverageBackfillEnabled()
2634
2630
  ? getTestContexts(originalTests)
2635
2631
  : undefined
2636
- coverageBackfillFiles = isSuitesSkipped && isTiaCoverageBackfillEnabled() && hasSkippableSuitesCoverage()
2632
+ coverageBackfillFiles = isSuitesSkipped && isTiaCoverageBackfillEnabled() && hasSkippableSuitesCoverage
2637
2633
  ? getCoverageBackfillFiles(skippableSuitesCoverage, repositoryRoot, getTestSuitePath)
2638
2634
  : undefined
2639
2635
 
@@ -3022,10 +3018,10 @@ function getCliWrapper (isNewJestVersion) {
3022
3018
  skippableSuitesCoverage: receivedSkippableSuitesCoverage,
3023
3019
  } = skippableSuitesResponse || await getChannelPromise(skippableSuitesCh)
3024
3020
  if (err) {
3025
- skippableSuitesCoverage = {}
3021
+ skippableSuitesCoverage = undefined
3026
3022
  } else {
3027
3023
  skippableSuites = receivedSkippableSuites
3028
- skippableSuitesCoverage = receivedSkippableSuitesCoverage || {}
3024
+ skippableSuitesCoverage = receivedSkippableSuitesCoverage
3029
3025
  }
3030
3026
  skippedSuitesCoverage = {}
3031
3027
  } catch (err) {
@@ -3066,7 +3062,19 @@ function getCliWrapper (isNewJestVersion) {
3066
3062
  frameworkVersion: jestVersion,
3067
3063
  })
3068
3064
 
3069
- const result = await runCLI.apply(this, arguments)
3065
+ let result
3066
+ try {
3067
+ result = await runCLI.apply(this, arguments)
3068
+ } catch (error) {
3069
+ try {
3070
+ await waitForTestSessionFinish(getTestSessionFinishPayload('fail', error, {
3071
+ isTestSessionFinalizationError: true,
3072
+ }))
3073
+ } catch (finalizationError) {
3074
+ log.error('Jest test session finalization error: %s', finalizationError)
3075
+ }
3076
+ throw error
3077
+ }
3070
3078
 
3071
3079
  const {
3072
3080
  results: {