dd-trace 6.9.0 → 6.10.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 (59) hide show
  1. package/ci/vitest-no-worker-init-setup.mjs +112 -10
  2. package/index.d.ts +42 -0
  3. package/package.json +4 -3
  4. package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +111 -6
  5. package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +629 -0
  6. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +2 -1
  7. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +81 -0
  8. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +1 -0
  9. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +83 -1
  10. package/packages/datadog-instrumentations/src/jest.js +1 -1
  11. package/packages/datadog-instrumentations/src/mocha/main.js +1 -1
  12. package/packages/datadog-instrumentations/src/mocha/utils.js +2 -8
  13. package/packages/datadog-instrumentations/src/mysql.js +45 -15
  14. package/packages/datadog-instrumentations/src/mysql2.js +119 -18
  15. package/packages/datadog-instrumentations/src/path-to-regexp.js +67 -1
  16. package/packages/datadog-instrumentations/src/pg.js +116 -92
  17. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +43 -4
  18. package/packages/datadog-instrumentations/src/vitest-main.js +169 -17
  19. package/packages/datadog-instrumentations/src/vitest-util.js +64 -5
  20. package/packages/datadog-instrumentations/src/vitest-worker.js +150 -25
  21. package/packages/datadog-instrumentations/src/webdriverio.js +31 -17
  22. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/trace-checkpoint.js +12 -6
  23. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +2 -5
  24. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -5
  25. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +1 -1
  26. package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +31 -17
  27. package/packages/datadog-plugin-jest/src/index.js +5 -4
  28. package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +2 -3
  29. package/packages/datadog-plugin-kafkajs/src/consumer.js +2 -3
  30. package/packages/datadog-plugin-kafkajs/src/producer.js +3 -2
  31. package/packages/datadog-plugin-kafkajs/src/utils.js +65 -7
  32. package/packages/datadog-plugin-mocha/src/index.js +550 -39
  33. package/packages/datadog-plugin-mysql/src/index.js +37 -0
  34. package/packages/datadog-plugin-next/src/index.js +1 -19
  35. package/packages/datadog-plugin-openai-agents/src/integration.js +278 -69
  36. package/packages/datadog-plugin-openai-agents/src/processor.js +16 -4
  37. package/packages/datadog-plugin-pg/src/index.js +1 -0
  38. package/packages/datadog-plugin-vitest/src/index.js +8 -20
  39. package/packages/datadog-plugin-ws/src/util.js +2 -1
  40. package/packages/dd-trace/src/appsec/api_security/normalized-route.js +453 -0
  41. package/packages/dd-trace/src/appsec/handlers/http-request.js +13 -0
  42. package/packages/dd-trace/src/carrier.js +356 -0
  43. package/packages/dd-trace/src/config/generated-config-types.d.ts +3 -0
  44. package/packages/dd-trace/src/config/supported-configurations.json +10 -0
  45. package/packages/dd-trace/src/datastreams/context.js +6 -1
  46. package/packages/dd-trace/src/datastreams/manager.js +5 -1
  47. package/packages/dd-trace/src/datastreams/pathway.js +23 -22
  48. package/packages/dd-trace/src/datastreams/processor.js +3 -2
  49. package/packages/dd-trace/src/llmobs/constants/tags.js +5 -0
  50. package/packages/dd-trace/src/llmobs/index.js +34 -2
  51. package/packages/dd-trace/src/llmobs/span_processor.js +16 -0
  52. package/packages/dd-trace/src/llmobs/tagger.js +96 -0
  53. package/packages/dd-trace/src/llmobs/util.js +78 -0
  54. package/packages/dd-trace/src/opentracing/propagation/text_map.js +126 -120
  55. package/packages/dd-trace/src/opentracing/propagation/text_map_dsm.js +4 -8
  56. package/packages/dd-trace/src/plugin_manager.js +2 -0
  57. package/packages/{datadog-plugin-jest/src/util.js → dd-trace/src/plugins/util/jest.js} +1 -62
  58. package/packages/dd-trace/src/plugins/util/test.js +84 -5
  59. package/packages/dd-trace/src/plugins/util/web.js +53 -2
@@ -3,6 +3,7 @@
3
3
  const path = require('node:path')
4
4
  const { performance } = require('node:perf_hooks')
5
5
  const { fileURLToPath } = require('node:url')
6
+ const { isMainThread, parentPort } = require('node:worker_threads')
6
7
 
7
8
  const shimmer = require('../../datadog-shimmer')
8
9
  const log = require('../../dd-trace/src/log')
@@ -12,6 +13,8 @@ const {
12
13
  getTestSuitePath,
13
14
  recordAttemptToFixExecution,
14
15
  logAttemptToFixTestExecution,
16
+ VITEST_WORKER_EFD_SUITE_ADMISSION_REQUEST_CODE,
17
+ VITEST_WORKER_EFD_SUITE_ADMISSION_RESPONSE_CODE,
15
18
  } = require('../../dd-trace/src/plugins/util/test')
16
19
  const { getChannelPromise } = require('./helpers/channel')
17
20
  const { addHook } = require('./helpers/instrument')
@@ -36,12 +39,15 @@ const {
36
39
  getVitestTestProperties,
37
40
  } = require('./vitest-util')
38
41
 
42
+ const EFD_SUITE_ADMISSION_TIMEOUT_MS = 5000
39
43
  const taskToCtx = new WeakMap()
40
44
  const taskToTestProperties = new WeakMap()
41
45
  const taskToStatuses = new WeakMap()
42
46
  const taskToReportedErrorCount = new WeakMap()
43
47
  const attemptToFixTaskToStatuses = new WeakMap()
44
48
  const fileToHasConcurrentTests = new WeakMap()
49
+ const fileToEfdSuiteAdmission = new WeakMap()
50
+ const pendingEfdSuiteAdmissionRequests = new Map()
45
51
  const originalHookFns = new WeakMap()
46
52
  const newTasks = new WeakSet()
47
53
  const dynamicNameTasks = new WeakSet()
@@ -62,6 +68,9 @@ let vitestGetFn = null
62
68
  let vitestSetFn = null
63
69
  let vitestGetHooks = null
64
70
  let preciseCoverageSession
71
+ let didInitializeEfdSuiteAdmissionTransport = false
72
+ let nextEfdSuiteAdmissionRequestId = 0
73
+ let sendEfdSuiteAdmissionMessage
65
74
  let isPreciseCoverageUnavailable = false
66
75
  let vitestCoverageSnapshot
67
76
  const wrappedCoverageWorkerStates = new WeakSet()
@@ -348,6 +357,123 @@ function hasConcurrentTests (file) {
348
357
  return hasConcurrent
349
358
  }
350
359
 
360
+ /**
361
+ * Returns whether a collected Vitest file contains a runnable new test eligible for EFD.
362
+ *
363
+ * @param {{ filepath: string, tasks?: object[] }} file
364
+ * @param {object} providedContext
365
+ * @returns {boolean}
366
+ */
367
+ function hasRunnableNewTest (file, providedContext) {
368
+ for (const task of getTypeTasks(file.tasks)) {
369
+ if (task.mode === 'skip' || task.mode === 'todo') continue
370
+
371
+ const testProperties = getVitestTestProperties(providedContext, file.filepath, getTestName(task))
372
+ if (testProperties.isNew && !testProperties.isAttemptToFix && !testProperties.isDisabled) {
373
+ return true
374
+ }
375
+ }
376
+ return false
377
+ }
378
+
379
+ /**
380
+ * Resolves and removes one pending EFD suite admission request.
381
+ *
382
+ * @param {number} requestId
383
+ * @param {boolean} allowed
384
+ * @returns {void}
385
+ */
386
+ function finishEfdSuiteAdmissionRequest (requestId, allowed) {
387
+ const request = pendingEfdSuiteAdmissionRequests.get(requestId)
388
+ if (!request) return
389
+
390
+ clearTimeout(request.timeout)
391
+ pendingEfdSuiteAdmissionRequests.delete(requestId)
392
+ request.resolve(allowed)
393
+ }
394
+
395
+ /**
396
+ * Handles an EFD suite admission response from the Vitest main process.
397
+ *
398
+ * @param {unknown} message
399
+ * @returns {void}
400
+ */
401
+ function handleEfdSuiteAdmissionResponse (message) {
402
+ if (!Array.isArray(message) || message[0] !== VITEST_WORKER_EFD_SUITE_ADMISSION_RESPONSE_CODE) return
403
+
404
+ const { allowed, requestId } = message[1] || {}
405
+ if (Number.isSafeInteger(requestId)) {
406
+ finishEfdSuiteAdmissionRequest(requestId, allowed === true)
407
+ }
408
+ }
409
+
410
+ /**
411
+ * Returns the child-process or worker-thread sender used by the current Vitest worker.
412
+ *
413
+ * @returns {((message: unknown) => void)|undefined}
414
+ */
415
+ function getEfdSuiteAdmissionSender () {
416
+ if (didInitializeEfdSuiteAdmissionTransport) return sendEfdSuiteAdmissionMessage
417
+ didInitializeEfdSuiteAdmissionTransport = true
418
+
419
+ if (typeof process.send === 'function') {
420
+ process.on('message', handleEfdSuiteAdmissionResponse)
421
+ sendEfdSuiteAdmissionMessage = process.send.bind(process)
422
+ } else if (!isMainThread && parentPort) {
423
+ parentPort.on('message', handleEfdSuiteAdmissionResponse)
424
+ sendEfdSuiteAdmissionMessage = parentPort.postMessage.bind(parentPort)
425
+ }
426
+ return sendEfdSuiteAdmissionMessage
427
+ }
428
+
429
+ /**
430
+ * Requests permission from the Vitest main process to schedule EFD retries for one suite.
431
+ *
432
+ * @param {string} testSuite
433
+ * @param {boolean} hasNewTest
434
+ * @returns {Promise<boolean>}
435
+ */
436
+ function requestEfdSuiteAdmission (testSuite, hasNewTest) {
437
+ const sendMessage = getEfdSuiteAdmissionSender()
438
+ if (!sendMessage) return Promise.resolve(false)
439
+
440
+ const requestId = ++nextEfdSuiteAdmissionRequestId
441
+ return new Promise(resolve => {
442
+ const timeout = setTimeout(
443
+ () => finishEfdSuiteAdmissionRequest(requestId, false),
444
+ EFD_SUITE_ADMISSION_TIMEOUT_MS
445
+ )
446
+ timeout.unref?.()
447
+ pendingEfdSuiteAdmissionRequests.set(requestId, { resolve, timeout })
448
+
449
+ try {
450
+ sendMessage([VITEST_WORKER_EFD_SUITE_ADMISSION_REQUEST_CODE, { hasNewTest, requestId, testSuite }])
451
+ } catch {
452
+ finishEfdSuiteAdmissionRequest(requestId, false)
453
+ }
454
+ })
455
+ }
456
+
457
+ /**
458
+ * Requests a single EFD admission decision for all tests collected in a Vitest file.
459
+ *
460
+ * @param {{ file: { filepath: string, tasks?: object[] } }} task
461
+ * @param {object} providedContext
462
+ * @param {string|undefined} testSuite
463
+ * @returns {Promise<boolean>}
464
+ */
465
+ function isEfdSuiteAdmissionAllowed (task, providedContext, testSuite) {
466
+ let admission = fileToEfdSuiteAdmission.get(task.file)
467
+ if (admission) return admission
468
+
469
+ admission = requestEfdSuiteAdmission(
470
+ testSuite || task.file.filepath,
471
+ hasRunnableNewTest(task.file, providedContext)
472
+ )
473
+ fileToEfdSuiteAdmission.set(task.file, admission)
474
+ return admission
475
+ }
476
+
351
477
  /**
352
478
  * Gets the task associated with a Vitest hook invocation.
353
479
  *
@@ -383,7 +509,7 @@ function wrapSuiteHookFn (hookType, fn, fallbackTask) {
383
509
  function wrapVitestTestRunner (VitestTestRunner) {
384
510
  // `onBeforeRunTask` is run before any repetition or attempt is run
385
511
  // `onBeforeRunTask` is an async function
386
- shimmer.wrap(VitestTestRunner.prototype, 'onBeforeRunTask', onBeforeRunTask => function (task) {
512
+ shimmer.wrap(VitestTestRunner.prototype, 'onBeforeRunTask', onBeforeRunTask => async function (task) {
387
513
  const testName = getTestName(task)
388
514
 
389
515
  const providedContext = getProvidedContext()
@@ -426,35 +552,38 @@ function wrapVitestTestRunner (VitestTestRunner) {
426
552
  }
427
553
 
428
554
  if (isImpactedTestsEnabled && testProperties.isModified) {
429
- if (isEarlyFlakeDetectionEnabled) {
430
- efdRetryTasks.add(task)
431
- disableFrameworkRetries(task)
432
- task.repeats = earlyFlakeDetectionRetryPolicy.schedulingRetryCount
433
- }
434
555
  modifiedTasks.add(task)
435
- taskToStatuses.set(task, [])
436
556
  }
437
557
 
438
558
  if (isKnownTestsEnabled && testProperties.isNew && !attemptToFixTasks.has(task)) {
439
- if (isEarlyFlakeDetectionEnabled && !modifiedTasks.has(task)) {
440
- efdRetryTasks.add(task)
441
- disableFrameworkRetries(task)
442
- task.repeats = earlyFlakeDetectionRetryPolicy.schedulingRetryCount
443
- }
444
559
  newTasks.add(task)
445
- taskToStatuses.set(task, [])
446
560
  if (DYNAMIC_NAME_RE.test(testName)) {
447
561
  dynamicNameTasks.add(task)
448
562
  }
449
563
  }
450
564
 
565
+ const isEfdCandidate = isEarlyFlakeDetectionEnabled &&
566
+ !attemptToFixTasks.has(task) &&
567
+ !disabledTasks.has(task) &&
568
+ (modifiedTasks.has(task) || newTasks.has(task))
569
+ if (isEfdCandidate) {
570
+ const isAdmissionAllowed = !providedContext.isEfdSuiteAdmissionEnabled ||
571
+ await isEfdSuiteAdmissionAllowed(task, providedContext, testProperties.testSuite)
572
+ if (isAdmissionAllowed) {
573
+ efdRetryTasks.add(task)
574
+ disableFrameworkRetries(task)
575
+ task.repeats = earlyFlakeDetectionRetryPolicy.schedulingRetryCount
576
+ taskToStatuses.set(task, [])
577
+ }
578
+ }
579
+
451
580
  return onBeforeRunTask.apply(this, arguments)
452
581
  })
453
582
 
454
583
  // `onAfterRunTask` is run after all repetitions or attempts are run
455
584
  // `onAfterRunTask` is an async function
456
585
  shimmer.wrap(VitestTestRunner.prototype, 'onAfterRunTask', onAfterRunTask => function (task) {
457
- const { isEarlyFlakeDetectionEnabled, isTestManagementTestsEnabled } = getProvidedContext()
586
+ const { isTestManagementTestsEnabled } = getProvidedContext()
458
587
 
459
588
  if (isTestManagementTestsEnabled) {
460
589
  const isAttemptingToFix = attemptToFixTasks.has(task)
@@ -476,7 +605,7 @@ function wrapVitestTestRunner (VitestTestRunner) {
476
605
  }
477
606
  }
478
607
 
479
- if (isEarlyFlakeDetectionEnabled && taskToStatuses.has(task) && !attemptToFixTasks.has(task)) {
608
+ if (efdRetryTasks.has(task)) {
480
609
  const statuses = taskToStatuses.get(task)
481
610
  // If the test has passed at least once, we consider it passed
482
611
  if (statuses.includes('pass')) {
@@ -501,7 +630,6 @@ function wrapVitestTestRunner (VitestTestRunner) {
501
630
  const providedContext = getProvidedContext()
502
631
  const {
503
632
  isKnownTestsEnabled,
504
- isEarlyFlakeDetectionEnabled,
505
633
  isDiEnabled,
506
634
  earlyFlakeDetectionRetryPolicy,
507
635
  } = providedContext
@@ -512,7 +640,7 @@ function wrapVitestTestRunner (VitestTestRunner) {
512
640
 
513
641
  const { retry: numAttempt, repeats: numRepetition } = retryInfo
514
642
  const isFailedTestReplayAllowed = !hasConcurrentTests(task.file)
515
- const isEfdManagedTask = isEarlyFlakeDetectionEnabled && taskToStatuses.has(task) && !attemptToFixTasks.has(task)
643
+ const isEfdManagedTask = efdRetryTasks.has(task)
516
644
 
517
645
  if (isEfdManagedTask && numRepetition > 0 && !efdDeterminedRetries.has(task)) {
518
646
  const previousExecutionStart = efdExecutionStartByTask.get(task)
@@ -574,8 +702,9 @@ function wrapVitestTestRunner (VitestTestRunner) {
574
702
 
575
703
  const lastExecutionStatus = task.result.state
576
704
  const isAtf = attemptToFixTasks.has(task)
577
- const shouldTrackStatuses = isEarlyFlakeDetectionEnabled || isAtf
578
- const shouldFlipStatus = isEarlyFlakeDetectionEnabled || isAtf
705
+ const isEfd = efdRetryTasks.has(task)
706
+ const shouldTrackStatuses = isEfd || isAtf
707
+ const shouldFlipStatus = isEfd || isAtf
579
708
  const statuses = isAtf ? attemptToFixTaskToStatuses.get(task) : taskToStatuses.get(task)
580
709
 
581
710
  // These clauses handle task.repeats, whether EFD is enabled or not
@@ -697,7 +826,6 @@ function wrapVitestTestRunner (VitestTestRunner) {
697
826
  const result = await onAfterTryTask.apply(this, arguments)
698
827
 
699
828
  const {
700
- isEarlyFlakeDetectionEnabled,
701
829
  testManagementAttemptToFixRetries,
702
830
  earlyFlakeDetectionRetryPolicy,
703
831
  } = getProvidedContext()
@@ -719,10 +847,8 @@ function wrapVitestTestRunner (VitestTestRunner) {
719
847
  }
720
848
 
721
849
  if (
722
- isEarlyFlakeDetectionEnabled &&
850
+ efdRetryTasks.has(task) &&
723
851
  (retryInfo.repeats ?? 0) === 0 &&
724
- taskToStatuses.has(task) &&
725
- !attemptToFixTasks.has(task) &&
726
852
  !efdDeterminedRetries.has(task)
727
853
  ) {
728
854
  const executionStart = efdExecutionStartByTask.get(task)
@@ -921,8 +1047,7 @@ addHook({
921
1047
  }
922
1048
 
923
1049
  // Check if all EFD retries failed
924
- const isEfdRetry =
925
- providedContext.isEarlyFlakeDetectionEnabled && (newTasks.has(task) || modifiedTasks.has(task))
1050
+ const isEfdRetry = efdRetryTasks.has(task)
926
1051
  if (isEfdRetry) {
927
1052
  const statuses = taskToStatuses.get(task)
928
1053
  const efdRetryCount = efdDeterminedRetries.get(task) ??
@@ -171,7 +171,7 @@ function createWorkerConfiguration () {
171
171
  }
172
172
 
173
173
  /**
174
- * Configures a Jasmine worker for reporting-only execution and notifies its coordinator.
174
+ * Configures a Jasmine worker and waits for its coordinator-owned optimization settings.
175
175
  *
176
176
  * @param {{_jrunner?: {env?: {addReporter?: (reporter: object) => void}}, _specs?: string[]}} adapter
177
177
  * @param {{
@@ -185,13 +185,7 @@ function initializeJasmineWorker (adapter, context) {
185
185
  return
186
186
  }
187
187
 
188
- workerConfigurationCh.publish({
189
- libraryConfig: createWorkerConfiguration(),
190
- repositoryRoot: undefined,
191
- specs: adapter?._specs || [],
192
- testFramework: TEST_FRAMEWORK,
193
- testFrameworkAdapter: JASMINE_FRAMEWORK_ADAPTER,
194
- })
188
+ const specs = adapter?._specs || []
195
189
 
196
190
  if (jasmineDoneCh.hasSubscribers) {
197
191
  adapter?._jrunner?.env?.addReporter?.({
@@ -220,9 +214,10 @@ function initializeJasmineWorker (adapter, context) {
220
214
  /**
221
215
  * Releases Jasmine initialization exactly once.
222
216
  *
217
+ * @param {object} [configuration]
223
218
  * @returns {void}
224
219
  */
225
- function finish () {
220
+ function finish (configuration = createWorkerConfiguration()) {
226
221
  if (finished) {
227
222
  return
228
223
  }
@@ -230,6 +225,13 @@ function initializeJasmineWorker (adapter, context) {
230
225
  clearTimeout(timeout)
231
226
  process.off('message', onMessage)
232
227
  process.off('disconnect', finish)
228
+ workerConfigurationCh.publish({
229
+ libraryConfig: configuration,
230
+ repositoryRoot: configuration.repositoryRoot,
231
+ specs,
232
+ testFramework: TEST_FRAMEWORK,
233
+ testFrameworkAdapter: JASMINE_FRAMEWORK_ADAPTER,
234
+ })
233
235
  onDone()
234
236
  }
235
237
 
@@ -241,7 +243,7 @@ function initializeJasmineWorker (adapter, context) {
241
243
  */
242
244
  function onMessage (message) {
243
245
  if (message?.name === WORKER_READY_RESPONSE && message.content?.requestId === requestId) {
244
- finish()
246
+ finish(message.content.configuration)
245
247
  }
246
248
  }
247
249
 
@@ -446,6 +448,18 @@ function getMochaFrameworkData (frameworkData) {
446
448
  return frameworkData?.[TEST_FRAMEWORK] ?? frameworkData?.mocha
447
449
  }
448
450
 
451
+ /**
452
+ * Applies the worker configuration for an EFD session with faulty known-tests data.
453
+ *
454
+ * @param {object} configuration
455
+ * @returns {void}
456
+ */
457
+ function setEarlyFlakeDetectionFaulty (configuration) {
458
+ configuration.isEarlyFlakeDetectionEnabled = false
459
+ configuration.isEarlyFlakeDetectionFaulty = true
460
+ configuration.isKnownTestsEnabled = false
461
+ }
462
+
449
463
  /**
450
464
  * Applies settings and requests the enabled non-TIA datasets once for the whole run.
451
465
  *
@@ -498,9 +512,11 @@ function configureCoordinator (state, response) {
498
512
  pendingRequests++
499
513
  requestCoordinatorData(state, knownTestsCh, ({ err, knownTests } = {}) => {
500
514
  const mochaKnownTests = getMochaFrameworkData(knownTests)
501
- if (err || !mochaKnownTests) {
515
+ if (err) {
502
516
  configuration.isEarlyFlakeDetectionEnabled = false
503
517
  configuration.isKnownTestsEnabled = false
518
+ } else if (mochaKnownTests === undefined) {
519
+ setEarlyFlakeDetectionFaulty(configuration)
504
520
  } else {
505
521
  configuration.knownTests = { mocha: mochaKnownTests }
506
522
  }
@@ -564,7 +580,6 @@ function initializeCoordinator (state, onDone) {
564
580
 
565
581
  try {
566
582
  libraryConfigurationCh.runStores({
567
- basicReportingOnly: state.testFrameworkAdapter === JASMINE_FRAMEWORK_ADAPTER,
568
583
  disableTestImpactAnalysis: true,
569
584
  frameworkVersion: state.frameworkVersion,
570
585
  isParallel: state.maxActiveWorkers > 1,
@@ -727,9 +742,7 @@ function updateEarlyFlakeDetectionFaultyState (state, files) {
727
742
  configuration.earlyFlakeDetectionFaultyThreshold
728
743
  )
729
744
  if (isFaulty) {
730
- configuration.isEarlyFlakeDetectionEnabled = false
731
- configuration.isEarlyFlakeDetectionFaulty = true
732
- configuration.isKnownTestsEnabled = false
745
+ setEarlyFlakeDetectionFaulty(configuration)
733
746
  }
734
747
  }
735
748
 
@@ -809,15 +822,16 @@ function handleWorkerMessage (state, workerRecord, message) {
809
822
  }
810
823
 
811
824
  if (message.name === WORKER_READY) {
812
- initializeCoordinator(state, () => {
825
+ initializeCoordinator(state, configuration => {
813
826
  if (state.testFrameworkAdapter === JASMINE_FRAMEWORK_ADAPTER) {
827
+ updateEarlyFlakeDetectionFaultyState(state, workerRecord.specs)
814
828
  startWorkerSuites(workerRecord, workerRecord.specs)
815
829
  }
816
830
  if (message.content?.requestId) {
817
831
  sendWorkerMessage(workerRecord, {
818
832
  origin: 'datadog',
819
833
  name: WORKER_READY_RESPONSE,
820
- content: { requestId: message.content.requestId },
834
+ content: { configuration, requestId: message.content.requestId },
821
835
  })
822
836
  }
823
837
  })
@@ -1,6 +1,12 @@
1
1
  'use strict'
2
2
 
3
3
  const crypto = require('crypto')
4
+ const {
5
+ deleteDatadogParentId,
6
+ readDatadogParentId,
7
+ readDatadogTraceId,
8
+ writeDatadogParentId,
9
+ } = require('../../dd-trace/src/carrier')
4
10
  const log = require('../../dd-trace/src/log')
5
11
  const TextMapPropagator = require('../../dd-trace/src/opentracing/propagation/text_map')
6
12
 
@@ -37,7 +43,7 @@ function getDatadogOnlyPropagator (tracer) {
37
43
  * @returns {Record<string, string>}
38
44
  */
39
45
  function injectHeaders (tracer, span) {
40
- const headers = {}
46
+ const headers = /** @type {Record<string, string>} */ ({})
41
47
  const propagator = getDatadogOnlyPropagator(tracer)
42
48
  const ctx = typeof span?.context === 'function' ? span.context() : span
43
49
  propagator.inject(ctx, headers)
@@ -51,8 +57,8 @@ function injectHeaders (tracer, span) {
51
57
  */
52
58
  function overrideParentId (headers, parentId) {
53
59
  if (!parentId) return
54
- if (headers['x-datadog-trace-id']) {
55
- headers['x-datadog-parent-id'] = String(parentId)
60
+ if (readDatadogTraceId(headers)) {
61
+ writeDatadogParentId(headers, String(parentId))
56
62
  }
57
63
  }
58
64
 
@@ -192,7 +198,7 @@ async function saveTraceContextCheckpointIfUpdated (
192
198
  if (typeof checkpointManager?.checkpoint !== 'function') return
193
199
 
194
200
  const currentHeaders = injectHeaders(tracer, span)
195
- if (currentHeaders['x-datadog-trace-id'] === undefined) return
201
+ if (readDatadogTraceId(currentHeaders) === undefined) return
196
202
 
197
203
  const latest = findLastCheckpoint(event)
198
204
 
@@ -204,8 +210,8 @@ async function saveTraceContextCheckpointIfUpdated (
204
210
  // x-datadog-parent-id reflects the active span at save time and always differs, so exclude it
205
211
  // from the comparison. Capture the previous anchor first to carry it forward on a real update.
206
212
  // needsCheckpointUpdate only reads currentHeaders' keys, so deleting it from there is enough.
207
- const anchoredSpanId = previousHeaders['x-datadog-parent-id']
208
- delete currentHeaders['x-datadog-parent-id']
213
+ const anchoredSpanId = readDatadogParentId(previousHeaders)
214
+ deleteDatadogParentId(currentHeaders)
209
215
  if (!needsCheckpointUpdate(currentHeaders, previousHeaders)) return
210
216
 
211
217
  newNumber = latest.checkpointNumber + 1
@@ -196,14 +196,11 @@ class Kinesis extends BaseAwsSdkPlugin {
196
196
  try {
197
197
  parsedAttributes = JSON.parse(recordDataAsString(record.Data))
198
198
  } catch {
199
- // Non-JSON record. Skip DSM context for this entry; the
200
- // checkpoint payload size below is still reported.
199
+ // Non-JSON record: it carries no context, so the checkpoint below starts a new pathway.
201
200
  }
202
201
 
203
202
  const payloadSize = getSizeOrZero(record.Data)
204
- if (parsedAttributes?._datadog) {
205
- this.tracer.decodeDataStreamsContext(parsedAttributes._datadog)
206
- }
203
+ this.tracer.decodeDataStreamsContext(parsedAttributes?._datadog)
207
204
  this.tracer.setCheckpoint(tags, span, payloadSize)
208
205
  }
209
206
  }
@@ -275,11 +275,7 @@ class Sqs extends BaseAwsSdkPlugin {
275
275
  for (let i = 0; i < messages.length; i++) {
276
276
  const message = messages[i]
277
277
  const carrier = carriers === undefined ? this.parseMessageCarrier(message) : carriers[i]
278
- if (carrier) {
279
- // Inert for EventBridge until its producer emits a pathway (separate
280
- // change) — no `dd-pathway-ctx-base64` to decode yet; SQS/SNS decode now.
281
- this.tracer.decodeDataStreamsContext(carrier)
282
- }
278
+ this.tracer.decodeDataStreamsContext(carrier)
283
279
  const payloadSize = getHeadersSize({
284
280
  Body: message.Body,
285
281
  MessageAttributes: message.MessageAttributes,
@@ -78,6 +78,7 @@ const {
78
78
  TEST_HAS_DYNAMIC_NAME,
79
79
  getIsFaultyEarlyFlakeDetection,
80
80
  DYNAMIC_NAME_RE,
81
+ isMarkedAsUnskippable,
81
82
  recordAttemptToFixExecution,
82
83
  recordTestManagementExecution,
83
84
  logAttemptToFixTestExecution,
@@ -86,7 +87,6 @@ const {
86
87
  TEST_FINAL_STATUS,
87
88
  getTestOptimizationRequestResults,
88
89
  } = require('../../dd-trace/src/plugins/util/test')
89
- const { isMarkedAsUnskippable } = require('../../datadog-plugin-jest/src/util')
90
90
  const { ORIGIN_KEY, COMPONENT } = require('../../dd-trace/src/constants')
91
91
  const { RESOURCE_NAME } = require('../../../ext/tags')
92
92
  const getConfig = require('../../dd-trace/src/config')
@@ -1,8 +1,17 @@
1
1
  'use strict'
2
2
 
3
- const ProducerPlugin = require('../../dd-trace/src/plugins/producer')
3
+ const {
4
+ readDatadogParentId,
5
+ readDatadogSamplingPriority,
6
+ readDatadogTraceId,
7
+ readTraceparent,
8
+ writeDatadogParentId,
9
+ writeDatadogSamplingPriority,
10
+ writeDatadogTraceId,
11
+ } = require('../../dd-trace/src/carrier')
4
12
  const { DsmPathwayCodec, getHeadersSize } = require('../../dd-trace/src/datastreams')
5
13
  const id = require('../../dd-trace/src/id')
14
+ const ProducerPlugin = require('../../dd-trace/src/plugins/producer')
6
15
 
7
16
  class GoogleCloudPubsubProducerPlugin extends ProducerPlugin {
8
17
  static id = 'google-cloud-pubsub'
@@ -21,7 +30,7 @@ class GoogleCloudPubsubProducerPlugin extends ProducerPlugin {
21
30
 
22
31
  handleMessagePublish ({ attributes, pubsub, topicName }) {
23
32
  // Skip if message already has trace context from upstream
24
- if (attributes['x-datadog-trace-id'] || attributes.traceparent) return
33
+ if (readDatadogTraceId(attributes) || readTraceparent(attributes)) return
25
34
 
26
35
  const activeSpan = this.tracer.scope().active()
27
36
  if (!activeSpan) return
@@ -62,7 +71,7 @@ class GoogleCloudPubsubProducerPlugin extends ProducerPlugin {
62
71
  const messages = request.messages || []
63
72
  const topic = request.topic
64
73
  const messageCount = messages.length
65
- const hasTraceContext = messages[0]?.attributes?.['x-datadog-trace-id']
74
+ const hasTraceContext = messages[0]?.attributes && readDatadogTraceId(messages[0].attributes)
66
75
 
67
76
  /**
68
77
  * Batch Publishing Strategy:
@@ -82,12 +91,14 @@ class GoogleCloudPubsubProducerPlugin extends ProducerPlugin {
82
91
  }
83
92
 
84
93
  const firstAttrs = messages[0]?.attributes
85
- const parentData = firstAttrs?.['x-datadog-trace-id'] && firstAttrs['x-datadog-parent-id']
94
+ const traceId = firstAttrs && readDatadogTraceId(firstAttrs)
95
+ const spanId = firstAttrs && readDatadogParentId(firstAttrs)
96
+ const parentData = traceId && spanId
86
97
  ? {
87
- traceId: firstAttrs['x-datadog-trace-id'],
88
- spanId: firstAttrs['x-datadog-parent-id'],
98
+ traceId,
99
+ spanId,
89
100
  traceIdUpper: firstAttrs['_dd.p.tid'],
90
- samplingPriority: firstAttrs['x-datadog-sampling-priority'],
101
+ samplingPriority: readDatadogSamplingPriority(firstAttrs),
91
102
  }
92
103
  : null
93
104
 
@@ -174,33 +185,36 @@ class GoogleCloudPubsubProducerPlugin extends ProducerPlugin {
174
185
  }
175
186
 
176
187
  #extractSpanLink (attrs) {
177
- if (!attrs?.['x-datadog-trace-id'] || !attrs['x-datadog-parent-id']) return null
188
+ if (!attrs) return null
189
+ const traceId = readDatadogTraceId(attrs)
190
+ const spanId = readDatadogParentId(attrs)
191
+ if (!traceId || !spanId) return null
178
192
 
179
193
  // Convert to hex strings
180
- const lowerHex = id(attrs['x-datadog-trace-id']).toString(16)
181
- const spanIdHex = id(attrs['x-datadog-parent-id']).toString(16)
194
+ const lowerHex = id(traceId).toString(16)
195
+ const spanIdHex = id(spanId).toString(16)
182
196
 
183
197
  // Build full 128-bit trace ID
184
198
  const traceIdHex = attrs['_dd.p.tid']
185
199
  ? attrs['_dd.p.tid'] + lowerHex
186
200
  : lowerHex.padStart(32, '0')
187
201
 
202
+ const samplingPriority = readDatadogSamplingPriority(attrs)
188
203
  return {
189
204
  traceId: traceIdHex,
190
205
  spanId: spanIdHex,
191
- samplingPriority: attrs['x-datadog-sampling-priority']
192
- ? Number.parseInt(attrs['x-datadog-sampling-priority'], 10)
206
+ samplingPriority: samplingPriority
207
+ ? Number.parseInt(samplingPriority, 10)
193
208
  : undefined,
194
209
  }
195
210
  }
196
211
 
197
212
  #extractParentContext (data) {
198
- const carrier = {
199
- 'x-datadog-trace-id': data.traceId,
200
- 'x-datadog-parent-id': data.spanId,
201
- }
213
+ const carrier = /** @type {Record<string, unknown>} */ ({})
214
+ writeDatadogTraceId(carrier, data.traceId)
215
+ writeDatadogParentId(carrier, data.spanId)
202
216
  if (data.traceIdUpper) carrier['_dd.p.tid'] = data.traceIdUpper
203
- if (data.samplingPriority) carrier['x-datadog-sampling-priority'] = String(data.samplingPriority)
217
+ if (data.samplingPriority) writeDatadogSamplingPriority(carrier, String(data.samplingPriority))
204
218
 
205
219
  return this.tracer.extract('text_map', carrier)
206
220
  }
@@ -5,6 +5,7 @@ const realSetTimeout = setTimeout
5
5
 
6
6
  const CiPlugin = require('../../dd-trace/src/plugins/ci_plugin')
7
7
  const { storage } = require('../../datadog-core')
8
+ const { writeDatadogParentId, writeDatadogTraceId } = require('../../dd-trace/src/carrier')
8
9
  const { getEnvironmentVariable } = require('../../dd-trace/src/config/helper')
9
10
  const { appClosing: appClosingTelemetry } = require('../../dd-trace/src/telemetry')
10
11
 
@@ -230,10 +231,10 @@ class JestPlugin extends CiPlugin {
230
231
  _ddItrSkippingEnabledTags: itrSkippingEnabledTags,
231
232
  } = testEnvironmentOptions
232
233
 
233
- const testSessionSpanContext = this.tracer.extract('text_map', {
234
- 'x-datadog-trace-id': testSessionId,
235
- 'x-datadog-parent-id': testModuleId,
236
- })
234
+ const carrier = /** @type {Record<string, unknown>} */ ({})
235
+ writeDatadogTraceId(carrier, testSessionId)
236
+ writeDatadogParentId(carrier, testModuleId)
237
+ const testSessionSpanContext = this.tracer.extract('text_map', carrier)
237
238
 
238
239
  const testSuiteMetadata = {
239
240
  ...getTestSuiteCommonTags(testCommand, frameworkVersion, testSuite, 'jest'),
@@ -1,8 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const ConsumerPlugin = require('../../dd-trace/src/plugins/consumer')
4
- const { getMessageSize } = require('../../dd-trace/src/datastreams')
5
- const { convertToTextMap } = require('./utils')
4
+ const { convertToTextMap, getKafkaMessageSize } = require('./utils')
6
5
 
7
6
  class KafkajsBatchConsumerPlugin extends ConsumerPlugin {
8
7
  static id = 'kafkajs'
@@ -39,7 +38,7 @@ class KafkajsBatchConsumerPlugin extends ConsumerPlugin {
39
38
  }
40
39
 
41
40
  if (!this.config.dsmEnabled) continue
42
- const payloadSize = getMessageSize(message)
41
+ const payloadSize = getKafkaMessageSize(message)
43
42
  this.tracer.decodeDataStreamsContext(headers)
44
43
  const edgeTags = ['direction:in', `group:${groupId}`, `topic:${topic}`, 'type:kafka']
45
44
  if (clusterId) {