dd-trace 5.96.0 → 5.98.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/index.d.ts +60 -2
  2. package/package.json +9 -7
  3. package/packages/datadog-esbuild/index.js +20 -9
  4. package/packages/datadog-instrumentations/src/child_process.js +7 -17
  5. package/packages/datadog-instrumentations/src/crypto.js +1 -2
  6. package/packages/datadog-instrumentations/src/cucumber.js +69 -4
  7. package/packages/datadog-instrumentations/src/cypress-config.js +318 -0
  8. package/packages/datadog-instrumentations/src/cypress.js +86 -4
  9. package/packages/datadog-instrumentations/src/dns.js +1 -2
  10. package/packages/datadog-instrumentations/src/express.js +4 -4
  11. package/packages/datadog-instrumentations/src/fs.js +27 -29
  12. package/packages/datadog-instrumentations/src/graphql.js +1 -1
  13. package/packages/datadog-instrumentations/src/helpers/bundler-register.js +41 -13
  14. package/packages/datadog-instrumentations/src/helpers/hook.js +31 -6
  15. package/packages/datadog-instrumentations/src/helpers/hooks.js +12 -19
  16. package/packages/datadog-instrumentations/src/helpers/instrument.js +27 -13
  17. package/packages/datadog-instrumentations/src/helpers/register.js +103 -142
  18. package/packages/datadog-instrumentations/src/http/client.js +2 -3
  19. package/packages/datadog-instrumentations/src/http/server.js +2 -5
  20. package/packages/datadog-instrumentations/src/http2/client.js +1 -3
  21. package/packages/datadog-instrumentations/src/http2/server.js +1 -3
  22. package/packages/datadog-instrumentations/src/jest.js +117 -16
  23. package/packages/datadog-instrumentations/src/limitd-client.js +1 -1
  24. package/packages/datadog-instrumentations/src/mocha/utils.js +12 -1
  25. package/packages/datadog-instrumentations/src/net.js +2 -8
  26. package/packages/datadog-instrumentations/src/pino.js +1 -1
  27. package/packages/datadog-instrumentations/src/playwright.js +4 -1
  28. package/packages/datadog-instrumentations/src/prisma.js +1 -2
  29. package/packages/datadog-instrumentations/src/redis.js +12 -6
  30. package/packages/datadog-instrumentations/src/selenium.js +4 -1
  31. package/packages/datadog-instrumentations/src/sequelize.js +1 -1
  32. package/packages/datadog-instrumentations/src/url.js +1 -3
  33. package/packages/datadog-instrumentations/src/vitest.js +5 -1
  34. package/packages/datadog-instrumentations/src/vm.js +1 -3
  35. package/packages/datadog-plugin-aws-sdk/src/base.js +5 -4
  36. package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +1 -0
  37. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +1 -0
  38. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -0
  39. package/packages/datadog-plugin-aws-sdk/src/services/redshift.js +1 -0
  40. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +1 -0
  41. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -0
  42. package/packages/datadog-plugin-cucumber/src/index.js +13 -3
  43. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +166 -6
  44. package/packages/datadog-plugin-cypress/src/index.js +59 -2
  45. package/packages/datadog-plugin-fs/src/index.js +1 -1
  46. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -1
  47. package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +2 -7
  48. package/packages/datadog-plugin-graphql/src/resolve.js +1 -1
  49. package/packages/datadog-plugin-http/src/client.js +1 -1
  50. package/packages/datadog-plugin-http/src/server.js +10 -2
  51. package/packages/datadog-plugin-http2/src/client.js +1 -1
  52. package/packages/datadog-plugin-http2/src/server.js +10 -2
  53. package/packages/datadog-plugin-jest/src/index.js +4 -2
  54. package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +31 -4
  55. package/packages/datadog-plugin-mocha/src/index.js +5 -2
  56. package/packages/datadog-plugin-mongodb-core/src/index.js +3 -3
  57. package/packages/datadog-plugin-mysql/src/index.js +1 -1
  58. package/packages/datadog-plugin-next/src/index.js +10 -16
  59. package/packages/datadog-plugin-openai/src/services.js +1 -0
  60. package/packages/datadog-plugin-pg/src/index.js +1 -1
  61. package/packages/datadog-plugin-tedious/src/index.js +1 -1
  62. package/packages/datadog-plugin-ws/src/close.js +1 -1
  63. package/packages/datadog-plugin-ws/src/receiver.js +1 -1
  64. package/packages/datadog-webpack/index.js +3 -3
  65. package/packages/dd-trace/index.js +12 -10
  66. package/packages/dd-trace/src/agent/url.js +2 -2
  67. package/packages/dd-trace/src/aiguard/sdk.js +26 -22
  68. package/packages/dd-trace/src/appsec/blocked_templates.js +4 -3
  69. package/packages/dd-trace/src/appsec/blocking.js +64 -33
  70. package/packages/dd-trace/src/appsec/iast/iast-plugin.js +1 -1
  71. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +1 -1
  72. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +1 -1
  73. package/packages/dd-trace/src/appsec/remote_config.js +1 -0
  74. package/packages/dd-trace/src/appsec/sdk/index.js +4 -0
  75. package/packages/dd-trace/src/appsec/sdk/set_user.js +1 -1
  76. package/packages/dd-trace/src/appsec/sdk/track_event.js +5 -5
  77. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +2 -2
  78. package/packages/dd-trace/src/appsec/sdk/utils.js +4 -2
  79. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +6 -1
  80. package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +4 -0
  81. package/packages/dd-trace/src/config/defaults.js +315 -146
  82. package/packages/dd-trace/src/config/generated-config-types.d.ts +9 -1
  83. package/packages/dd-trace/src/config/helper.js +59 -10
  84. package/packages/dd-trace/src/config/index.js +587 -1496
  85. package/packages/dd-trace/src/config/parsers.js +256 -0
  86. package/packages/dd-trace/src/config/remote_config.js +59 -2
  87. package/packages/dd-trace/src/config/supported-configurations.json +406 -432
  88. package/packages/dd-trace/src/constants.js +1 -0
  89. package/packages/dd-trace/src/crashtracking/crashtracker.js +7 -1
  90. package/packages/dd-trace/src/crashtracking/index.js +1 -7
  91. package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +5 -2
  92. package/packages/dd-trace/src/debugger/index.js +1 -1
  93. package/packages/dd-trace/src/dogstatsd.js +12 -9
  94. package/packages/dd-trace/src/encode/0.4.js +8 -7
  95. package/packages/dd-trace/src/encode/span-stats.js +4 -1
  96. package/packages/dd-trace/src/exporters/agent/writer.js +7 -1
  97. package/packages/dd-trace/src/exporters/common/request.js +9 -0
  98. package/packages/dd-trace/src/exporters/common/writer.js +12 -2
  99. package/packages/dd-trace/src/heap_snapshots.js +3 -0
  100. package/packages/dd-trace/src/index.js +5 -2
  101. package/packages/dd-trace/src/lambda/runtime/ritm.js +6 -6
  102. package/packages/dd-trace/src/llmobs/index.js +4 -1
  103. package/packages/dd-trace/src/llmobs/plugins/ai/index.js +5 -1
  104. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +60 -12
  105. package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +4 -2
  106. package/packages/dd-trace/src/llmobs/sdk.js +12 -8
  107. package/packages/dd-trace/src/llmobs/span_processor.js +1 -1
  108. package/packages/dd-trace/src/llmobs/tagger.js +9 -6
  109. package/packages/dd-trace/src/llmobs/writers/base.js +2 -0
  110. package/packages/dd-trace/src/llmobs/writers/util.js +3 -0
  111. package/packages/dd-trace/src/log/index.js +20 -59
  112. package/packages/dd-trace/src/log/writer.js +7 -19
  113. package/packages/dd-trace/src/noop/proxy.js +8 -0
  114. package/packages/dd-trace/src/openfeature/remote_config.js +6 -1
  115. package/packages/dd-trace/src/opentelemetry/context_manager.js +6 -4
  116. package/packages/dd-trace/src/opentelemetry/logs/index.js +1 -1
  117. package/packages/dd-trace/src/opentelemetry/metrics/index.js +1 -1
  118. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +17 -2
  119. package/packages/dd-trace/src/opentelemetry/otlp/protobuf_loader.js +14 -2
  120. package/packages/dd-trace/src/opentelemetry/otlp/trace.proto +358 -0
  121. package/packages/dd-trace/src/opentelemetry/otlp/trace_service.proto +78 -0
  122. package/packages/dd-trace/src/opentelemetry/trace/index.js +75 -0
  123. package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +66 -0
  124. package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +332 -0
  125. package/packages/dd-trace/src/opentracing/propagation/text_map.js +9 -4
  126. package/packages/dd-trace/src/opentracing/tracer.js +9 -4
  127. package/packages/dd-trace/src/payload-tagging/config/index.js +6 -5
  128. package/packages/dd-trace/src/plugin_manager.js +8 -6
  129. package/packages/dd-trace/src/plugins/ci_plugin.js +4 -0
  130. package/packages/dd-trace/src/plugins/log_plugin.js +3 -0
  131. package/packages/dd-trace/src/plugins/plugin.js +11 -13
  132. package/packages/dd-trace/src/plugins/storage.js +2 -2
  133. package/packages/dd-trace/src/plugins/tracing.js +22 -5
  134. package/packages/dd-trace/src/plugins/util/test.js +2 -0
  135. package/packages/dd-trace/src/plugins/util/web.js +6 -88
  136. package/packages/dd-trace/src/process-tags/index.js +3 -0
  137. package/packages/dd-trace/src/profiler.js +27 -2
  138. package/packages/dd-trace/src/profiling/config.js +73 -241
  139. package/packages/dd-trace/src/profiling/exporter_cli.js +1 -4
  140. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +6 -2
  141. package/packages/dd-trace/src/profiling/profiler.js +78 -109
  142. package/packages/dd-trace/src/profiling/profilers/events.js +2 -3
  143. package/packages/dd-trace/src/profiling/profilers/wall.js +89 -6
  144. package/packages/dd-trace/src/profiling/ssi-heuristics.js +4 -1
  145. package/packages/dd-trace/src/propagation-hash/index.js +2 -1
  146. package/packages/dd-trace/src/proxy.js +40 -6
  147. package/packages/dd-trace/src/remote_config/index.js +3 -0
  148. package/packages/dd-trace/src/require-package-json.js +8 -4
  149. package/packages/dd-trace/src/ritm.js +58 -26
  150. package/packages/dd-trace/src/runtime_metrics/index.js +3 -0
  151. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +18 -11
  152. package/packages/dd-trace/src/sampler.js +1 -1
  153. package/packages/dd-trace/src/service-naming/index.js +1 -1
  154. package/packages/dd-trace/src/service-naming/schemas/definition.js +4 -1
  155. package/packages/dd-trace/src/service-naming/schemas/util.js +15 -1
  156. package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +24 -1
  157. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +60 -0
  158. package/packages/dd-trace/src/service-naming/schemas/v0/web.js +17 -1
  159. package/packages/dd-trace/src/service-naming/schemas/v0/websocket.js +5 -0
  160. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +17 -0
  161. package/packages/dd-trace/src/service-naming/schemas/v1/web.js +11 -1
  162. package/packages/dd-trace/src/service-naming/schemas/v1/websocket.js +6 -0
  163. package/packages/dd-trace/src/span_stats.js +5 -1
  164. package/packages/dd-trace/src/standalone/index.js +3 -0
  165. package/packages/dd-trace/src/telemetry/index.js +2 -3
  166. package/packages/dd-trace/src/telemetry/send-data.js +5 -19
  167. package/packages/dd-trace/src/telemetry/session-propagation.js +19 -44
  168. package/packages/dd-trace/src/telemetry/telemetry.js +28 -171
  169. package/packages/dd-trace/src/tracer.js +2 -2
  170. package/packages/dd-trace/src/util.js +0 -9
  171. package/vendor/dist/@apm-js-collab/code-transformer/index.js +28 -6
  172. package/vendor/dist/protobufjs/index.js +1 -1
  173. package/packages/dd-trace/src/log/utils.js +0 -16
package/index.d.ts CHANGED
@@ -130,6 +130,11 @@ interface Tracer extends opentracing.Tracer {
130
130
 
131
131
  appsec: tracer.Appsec;
132
132
 
133
+ /**
134
+ * Profiling API for attaching custom labels to profiler samples.
135
+ */
136
+ profiling: tracer.Profiling;
137
+
133
138
  TracerProvider: tracer.opentelemetry.TracerProvider;
134
139
 
135
140
  dogstatsd: tracer.DogStatsD;
@@ -660,7 +665,15 @@ declare namespace tracer {
660
665
  * @env DD_RUNTIME_METRICS_EVENT_LOOP_ENABLED
661
666
  * Programmatic configuration takes precedence over the environment variables listed above.
662
667
  */
663
- eventLoop?: boolean
668
+ eventLoop?: boolean,
669
+
670
+ /**
671
+ * Whether to use native metrics. When set to false, forces the JS implementation
672
+ * @default true
673
+ * @env DD_RUNTIME_METRICS_NATIVE
674
+ * Programmatic configuration takes precedence over the environment variables listed above.
675
+ */
676
+ native?: boolean
664
677
  }
665
678
 
666
679
  /**
@@ -790,7 +803,7 @@ declare namespace tracer {
790
803
  * Whether to request blocking mode when evaluating prompts via auto-instrumentation.
791
804
  * When `true`, AI Guard will block requests that violate security policies.
792
805
  * When `false`, AI Guard evaluates but never blocks (monitor-only mode).
793
- * @default false
806
+ * @default true
794
807
  * @env DD_AI_GUARD_BLOCK
795
808
  * Programmatic configuration takes precedence over the environment variables listed above.
796
809
  */
@@ -1570,6 +1583,35 @@ declare namespace tracer {
1570
1583
  trackUserLoginFailure(login: string, metadata?: any): void;
1571
1584
  }
1572
1585
 
1586
+ export interface Profiling {
1587
+ /**
1588
+ * Declares the set of custom label keys that will be used with
1589
+ * {@link runWithLabels}. This is used for profile upload metadata
1590
+ * (so the Datadog UI knows which keys to index for filtering) and
1591
+ * for pprof serialization optimization.
1592
+ *
1593
+ * @param keys Custom label key names.
1594
+ */
1595
+ setCustomLabelKeys(keys: Iterable<string>): void;
1596
+
1597
+ /**
1598
+ * Runs a function with custom profiling labels attached to all wall profiler
1599
+ * samples taken during its execution. Labels are key-value pairs that appear
1600
+ * in the pprof output and can be used to filter flame graphs in the Datadog UI.
1601
+ *
1602
+ * Requires AsyncContextFrame (ACF) to be enabled. Supports nesting: inner
1603
+ * calls merge labels with outer calls, with inner values taking precedence.
1604
+ *
1605
+ * When profiling is not enabled or ACF is not active, the function is still
1606
+ * called but labels are silently dropped.
1607
+ *
1608
+ * @param labels Custom labels to attach to profiler samples.
1609
+ * @param fn Function to execute with the labels.
1610
+ * @returns The return value of fn.
1611
+ */
1612
+ runWithLabels<T>(labels: Record<string, string | number>, fn: () => T): T;
1613
+ }
1614
+
1573
1615
  export interface Appsec {
1574
1616
  /**
1575
1617
  * Links a successful login event to the current trace. Will link the passed user to the current trace with Appsec.setUser() internally.
@@ -1813,6 +1855,10 @@ declare namespace tracer {
1813
1855
  * List of tags associated with the evaluation (e.g. indirect-prompt-injection)
1814
1856
  */
1815
1857
  tags: string[];
1858
+ /**
1859
+ * Dictionary of tag probabilities (e.g. { indirect-prompt-injection: 0.2, jailbreak-attempt: 0.8 })
1860
+ */
1861
+ tagProbabilities: { [key: string]: number }
1816
1862
  /**
1817
1863
  * Sensitive Data Scanner findings from the evaluation.
1818
1864
  */
@@ -1832,6 +1878,10 @@ declare namespace tracer {
1832
1878
  * List of tags associated with the evaluation (e.g. indirect-prompt-injection)
1833
1879
  */
1834
1880
  tags: string[];
1881
+ /**
1882
+ * Dictionary of tag probabilities (e.g. { indirect-prompt-injection: 0.2, jailbreak-attempt: 0.8 })
1883
+ */
1884
+ tagProbabilities: { [key: string]: number }
1835
1885
  /**
1836
1886
  * Sensitive Data Scanner findings from the evaluation.
1837
1887
  */
@@ -3014,6 +3064,14 @@ declare namespace tracer {
3014
3064
  * @returns true to instrument the command, false to skip it
3015
3065
  */
3016
3066
  filter?: (command: string) => boolean;
3067
+
3068
+ /**
3069
+ * Whether to use a different service name for each Redis instance based
3070
+ * on the configured connection name of the client.
3071
+ *
3072
+ * @default false
3073
+ */
3074
+ splitByInstance?: boolean;
3017
3075
  }
3018
3076
 
3019
3077
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dd-trace",
3
- "version": "5.96.0",
3
+ "version": "5.98.0",
4
4
  "description": "Datadog APM tracing client for JavaScript",
5
5
  "main": "index.js",
6
6
  "typings": "index.d.ts",
@@ -11,6 +11,8 @@
11
11
  "bench": "node benchmark/index.js",
12
12
  "bench:e2e:test-optimization": "node benchmark/e2e-test-optimization/benchmark-run.js",
13
13
  "dependencies:dedupe": "yarn-deduplicate yarn.lock",
14
+ "generate:config:types": "node scripts/generate-config-types.js",
15
+ "verify:config:types": "node scripts/generate-config-types.js --check",
14
16
  "type:check": "tsc --noEmit -p tsconfig.dev.json",
15
17
  "type:doc:build": "cd docs && yarn && yarn build",
16
18
  "type:doc:test": "cd docs && yarn && yarn test",
@@ -142,7 +144,7 @@
142
144
  "import-in-the-middle": "^3.0.1"
143
145
  },
144
146
  "optionalDependencies": {
145
- "@datadog/libdatadog": "0.9.2",
147
+ "@datadog/libdatadog": "0.9.3",
146
148
  "@datadog/native-appsec": "11.0.1",
147
149
  "@datadog/native-iast-taint-tracking": "4.1.0",
148
150
  "@datadog/native-metrics": "3.1.1",
@@ -166,19 +168,19 @@
166
168
  "@types/mocha": "^10.0.10",
167
169
  "@types/node": "^18.19.106",
168
170
  "@types/sinon": "^21.0.0",
169
- "axios": "^1.13.4",
171
+ "axios": "^1.15.0",
170
172
  "benchmark": "^2.1.4",
171
173
  "body-parser": "^2.2.2",
172
174
  "bun": "1.3.11",
173
175
  "codeowners-audit": "^2.9.0",
174
176
  "eslint": "^9.39.2",
175
- "eslint-plugin-cypress": "^6.2.1",
177
+ "eslint-plugin-cypress": "^6.2.2",
176
178
  "eslint-plugin-import": "^2.32.0",
177
- "eslint-plugin-jsdoc": "^62.8.1",
179
+ "eslint-plugin-jsdoc": "^62.9.0",
178
180
  "eslint-plugin-mocha": "^11.2.0",
179
181
  "eslint-plugin-n": "^17.23.2",
180
182
  "eslint-plugin-promise": "^7.2.1",
181
- "eslint-plugin-unicorn": "^63.0.0",
183
+ "eslint-plugin-unicorn": "^64.0.0",
182
184
  "express": "^5.1.0",
183
185
  "glob": "^10.4.5",
184
186
  "globals": "^17.2.0",
@@ -200,7 +202,7 @@
200
202
  "semver": "^7.7.2",
201
203
  "sinon": "^21.0.3",
202
204
  "tiktoken": "^1.0.21",
203
- "typescript": "^5.9.2",
205
+ "typescript": "^6.0.2",
204
206
  "workerpool": "^10.0.0",
205
207
  "yaml": "^2.8.3",
206
208
  "yarn-deduplicate": "^6.0.2"
@@ -2,7 +2,6 @@
2
2
 
3
3
  const { execSync } = require('node:child_process')
4
4
  const fs = require('node:fs')
5
- const RAW_BUILTINS = require('node:module').builtinModules
6
5
  const path = require('node:path')
7
6
  const { pathToFileURL, fileURLToPath } = require('node:url')
8
7
 
@@ -25,15 +24,27 @@ for (const hook of Object.values(hooks)) {
25
24
  }
26
25
  }
27
26
 
27
+ function moduleOfInterestKey (name, file) {
28
+ return file ? `${name}/${file}` : name
29
+ }
30
+
31
+ const builtinModules = new Set(require('module').builtinModules)
32
+
33
+ function addModuleOfInterest (name, file) {
34
+ if (!name) return
35
+
36
+ modulesOfInterest.add(moduleOfInterestKey(name, file))
37
+
38
+ if (builtinModules.has(name)) {
39
+ modulesOfInterest.add(moduleOfInterestKey(`node:${name}`, file))
40
+ }
41
+ }
42
+
28
43
  const modulesOfInterest = new Set()
29
44
 
30
- for (const instrumentation of Object.values(instrumentations)) {
45
+ for (const [name, instrumentation] of Object.entries(instrumentations)) {
31
46
  for (const entry of instrumentation) {
32
- if (entry.file) {
33
- modulesOfInterest.add(`${entry.name}/${entry.file}`) // e.g. "redis/my/file.js"
34
- } else {
35
- modulesOfInterest.add(entry.name) // e.g. "redis"
36
- }
47
+ addModuleOfInterest(name, entry.file)
37
48
  }
38
49
  }
39
50
 
@@ -41,7 +52,7 @@ const CHANNEL = 'dd-trace:bundler:load'
41
52
 
42
53
  const builtins = new Set()
43
54
 
44
- for (const builtin of RAW_BUILTINS) {
55
+ for (const builtin of builtinModules) {
45
56
  builtins.add(builtin)
46
57
  builtins.add(`node:${builtin}`)
47
58
  }
@@ -247,7 +258,7 @@ ${build.initialOptions.banner.js}`
247
258
  }
248
259
 
249
260
  try {
250
- const packageJson = JSON.parse(fs.readFileSync(/** @type {string} */ (pathToPackageJson)).toString())
261
+ const packageJson = JSON.parse(fs.readFileSync(/** @type {string} */(pathToPackageJson)).toString())
251
262
 
252
263
  const isESM = isESMFile(fullPathToModule, pathToPackageJson, packageJson)
253
264
  if (isESM && !interceptedESMModules.has(fullPathToModule)) {
@@ -14,11 +14,6 @@ const childProcessChannel = dc.tracingChannel('datadog:child_process:execution')
14
14
  // ignored exec method because it calls to execFile directly
15
15
  const execAsyncMethods = ['execFile', 'spawn', 'fork']
16
16
 
17
- const names = ['child_process', 'node:child_process']
18
-
19
- // child_process and node:child_process returns the same object instance, we only want to add hooks once
20
- let patched = false
21
-
22
17
  function throwSyncError (error) {
23
18
  throw error
24
19
  }
@@ -37,19 +32,14 @@ function returnSpawnSyncError (error, context) {
37
32
  return context.result
38
33
  }
39
34
 
40
- for (const name of names) {
41
- addHook({ name }, childProcess => {
42
- if (!patched) {
43
- patched = true
44
- shimmer.massWrap(childProcess, execAsyncMethods, wrapChildProcessAsyncMethod(childProcess.ChildProcess))
45
- shimmer.wrap(childProcess, 'execSync', wrapChildProcessSyncMethod(throwSyncError, true))
46
- shimmer.wrap(childProcess, 'execFileSync', wrapChildProcessSyncMethod(throwSyncError))
47
- shimmer.wrap(childProcess, 'spawnSync', wrapChildProcessSyncMethod(returnSpawnSyncError))
48
- }
35
+ addHook({ name: 'child_process' }, childProcess => {
36
+ shimmer.massWrap(childProcess, execAsyncMethods, wrapChildProcessAsyncMethod(childProcess.ChildProcess))
37
+ shimmer.wrap(childProcess, 'execSync', wrapChildProcessSyncMethod(throwSyncError, true))
38
+ shimmer.wrap(childProcess, 'execFileSync', wrapChildProcessSyncMethod(throwSyncError))
39
+ shimmer.wrap(childProcess, 'spawnSync', wrapChildProcessSyncMethod(returnSpawnSyncError))
49
40
 
50
- return childProcess
51
- })
52
- }
41
+ return childProcess
42
+ })
53
43
 
54
44
  function normalizeArgs (args, shell) {
55
45
  const childProcessInfo = {
@@ -11,9 +11,8 @@ const cryptoCipherCh = channel('datadog:crypto:cipher:start')
11
11
 
12
12
  const hashMethods = ['createHash', 'createHmac', 'createSign', 'createVerify', 'sign', 'verify']
13
13
  const cipherMethods = ['createCipheriv', 'createDecipheriv']
14
- const names = ['crypto', 'node:crypto']
15
14
 
16
- addHook({ name: names }, crypto => {
15
+ addHook({ name: 'crypto' }, crypto => {
17
16
  shimmer.massWrap(crypto, hashMethods, wrapCryptoMethod(cryptoHashCh))
18
17
  shimmer.massWrap(crypto, cipherMethods, wrapCryptoMethod(cryptoCipherCh))
19
18
  return crypto
@@ -61,6 +61,8 @@ const numRetriesByPickleId = new Map()
61
61
  const numAttemptToCtx = new Map()
62
62
  const newTestsByTestFullname = new Map()
63
63
  const modifiedTestsByPickleId = new Map()
64
+ // Pickle IDs for tests that are genuinely new (not in known tests list).
65
+ const newTestPickleIds = new Set()
64
66
 
65
67
  let eventDataCollector = null
66
68
  let pickleByFile = {}
@@ -233,6 +235,40 @@ function getPickleByFile (runtimeOrCoodinator) {
233
235
  }, {})
234
236
  }
235
237
 
238
+ function getFinalStatus ({
239
+ status,
240
+ hasFailedAllRetries,
241
+ isLastAtrRetry,
242
+ isLastEfdRetry,
243
+ isLastAttemptToFix,
244
+ hasPassedAllRetries,
245
+ isQuarantined,
246
+ isDisabled,
247
+ }) {
248
+ // Note that intermediate executions DO NOT report a final status tag
249
+
250
+ // If the test is quarantined or disabled, regardless of its actual execution result or active retry features,
251
+ // the final status of its last execution should be reported as 'skip'.
252
+ if (isQuarantined || isDisabled || status === 'skip') {
253
+ return 'skip'
254
+ }
255
+
256
+ // When no retry feature is active, every execution is final
257
+ if (!isLastAtrRetry && !isLastEfdRetry && !isLastAttemptToFix) {
258
+ return status
259
+ }
260
+
261
+ // ATR and EFD: pass unless every attempt failed
262
+ if (isLastAtrRetry || isLastEfdRetry) {
263
+ return hasFailedAllRetries ? 'fail' : 'pass'
264
+ }
265
+
266
+ // Branch for ATF (We need to check hasPassedAllRetries)
267
+ if (isLastAttemptToFix) {
268
+ return hasPassedAllRetries ? 'pass' : 'fail'
269
+ }
270
+ }
271
+
236
272
  function wrapRun (pl, isLatestVersion, version) {
237
273
  if (patched.has(pl)) return
238
274
 
@@ -260,7 +296,7 @@ function wrapRun (pl, isLatestVersion, version) {
260
296
  testStartCh.runStores(ctx, () => {})
261
297
  const promises = {}
262
298
  try {
263
- this.eventBroadcaster.on('envelope', async (testCase) => {
299
+ const onEnvelope = async (testCase) => {
264
300
  // Only supported from >=8.0.0
265
301
  if (testCase?.testCaseFinished) {
266
302
  const { testCaseFinished: { willBeRetried } } = testCase
@@ -278,7 +314,7 @@ function wrapRun (pl, isLatestVersion, version) {
278
314
  const isAtrRetry = !isFirstAttempt && isFlakyTestRetriesEnabled
279
315
 
280
316
  // ATR: record this attempt as failed so when run().finally runs (after retry) we have all statuses
281
- if (isFlakyTestRetriesEnabled && isAtrRetry === false) {
317
+ if (isFlakyTestRetriesEnabled) {
282
318
  const nameForKey = this.pickle.name.replace(/\s*\(attempt \d+(?:, retried)?\)\s*$/, '')
283
319
  const atrKey = `${this.pickle.uri}:${nameForKey}`
284
320
  if (atrStatusesByScenarioKey.has(atrKey)) {
@@ -301,13 +337,15 @@ function wrapRun (pl, isLatestVersion, version) {
301
337
  testStartCh.runStores(newCtx, () => {})
302
338
  }
303
339
  }
304
- })
340
+ }
341
+ this.eventBroadcaster.on('envelope', onEnvelope)
305
342
  let promise
306
343
 
307
344
  testFnCh.runStores(ctx, () => {
308
345
  promise = run.apply(this, arguments)
309
346
  })
310
347
  promise.finally(async () => {
348
+ this.eventBroadcaster.removeListener('envelope', onEnvelope)
311
349
  const result = this.getWorstStepResult()
312
350
  const { status, skipReason } = isLatestVersion
313
351
  ? getStatusFromResultLatest(result)
@@ -359,7 +397,7 @@ function wrapRun (pl, isLatestVersion, version) {
359
397
  }
360
398
 
361
399
  if (isKnownTestsEnabled && status !== 'skip') {
362
- isNew = numRetries !== undefined
400
+ isNew = newTestPickleIds.has(this.pickle.id)
363
401
  }
364
402
 
365
403
  if (isNew || isModified) {
@@ -406,6 +444,31 @@ function wrapRun (pl, isLatestVersion, version) {
406
444
  if (promises.hitBreakpointPromise) {
407
445
  await promises.hitBreakpointPromise
408
446
  }
447
+
448
+ // Notice that ATR is handled using cucumber native retries features.
449
+ // Therefore, if we reach this point, we are certain that it's the last ATR execution
450
+ const isLastAtrRetry = isFlakyTestRetriesEnabled && !isAttemptToFix && !isEfdRetry && numTestRetries > 0
451
+
452
+ const statuses = lastStatusByPickleId.get(this.pickle.id)
453
+ const isLastEfdRetry = isEfdRetry && statuses?.length === earlyFlakeDetectionNumRetries + 1
454
+ const isLastAttemptToFixRetry = isAttemptToFix && statuses?.length === testManagementAttemptToFixRetries + 1
455
+
456
+ // Intermediate (non-last EFD or ATF retries) executions do not report a final status
457
+ const isIntermediateExecution = (isEfdRetry && !isLastEfdRetry) || (isAttemptToFix && !isLastAttemptToFixRetry)
458
+
459
+ const finalStatus = isIntermediateExecution
460
+ ? undefined
461
+ : getFinalStatus({
462
+ status,
463
+ hasFailedAllRetries,
464
+ isLastAtrRetry,
465
+ isLastEfdRetry,
466
+ isLastAttemptToFix: isLastAttemptToFixRetry,
467
+ hasPassedAllRetries,
468
+ isQuarantined,
469
+ isDisabled,
470
+ })
471
+
409
472
  testFinishCh.publish({
410
473
  status,
411
474
  skipReason,
@@ -422,6 +485,7 @@ function wrapRun (pl, isLatestVersion, version) {
422
485
  isQuarantined,
423
486
  isModified,
424
487
  ...attemptCtx.currentStore,
488
+ finalStatus,
425
489
  })
426
490
  })
427
491
  return promise
@@ -714,6 +778,7 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
714
778
  if (isKnownTestsEnabled && !isAttemptToFix) {
715
779
  isNew = isNewTest(testSuitePath, pickle.name)
716
780
  if (isNew) {
781
+ newTestPickleIds.add(pickle.id)
717
782
  numRetriesByPickleId.set(pickle.id, 0)
718
783
  }
719
784
  }