dd-trace 5.110.0 → 5.112.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 (164) hide show
  1. package/LICENSE-3rdparty.csv +1 -2
  2. package/README.md +13 -8
  3. package/ci/init.js +21 -2
  4. package/ci/vitest-no-worker-init-setup.mjs +430 -0
  5. package/ext/tags.js +2 -0
  6. package/index.d.ts +34 -1
  7. package/initialize.mjs +5 -6
  8. package/loader-hook.mjs +76 -55
  9. package/package.json +37 -34
  10. package/packages/datadog-instrumentations/src/ai.js +45 -0
  11. package/packages/datadog-instrumentations/src/apollo-server.js +5 -13
  12. package/packages/datadog-instrumentations/src/child_process.js +3 -3
  13. package/packages/datadog-instrumentations/src/claude-agent-sdk.js +587 -0
  14. package/packages/datadog-instrumentations/src/cucumber.js +102 -43
  15. package/packages/datadog-instrumentations/src/cypress-config.js +11 -3
  16. package/packages/datadog-instrumentations/src/fastify.js +27 -8
  17. package/packages/datadog-instrumentations/src/fs.js +8 -6
  18. package/packages/datadog-instrumentations/src/graphql.js +26 -495
  19. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  20. package/packages/datadog-instrumentations/src/helpers/instrument.js +7 -0
  21. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +6 -6
  22. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/claude-agent-sdk.js +17 -0
  23. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql.js +201 -0
  24. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +2 -0
  25. package/packages/datadog-instrumentations/src/jest.js +713 -63
  26. package/packages/datadog-instrumentations/src/mocha/main.js +24 -3
  27. package/packages/datadog-instrumentations/src/mocha/utils.js +128 -22
  28. package/packages/datadog-instrumentations/src/mocha/worker.js +13 -0
  29. package/packages/datadog-instrumentations/src/mongodb.js +74 -0
  30. package/packages/datadog-instrumentations/src/mongoose.js +13 -2
  31. package/packages/datadog-instrumentations/src/playwright.js +13 -9
  32. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +1297 -0
  33. package/packages/datadog-instrumentations/src/vitest-main.js +1594 -0
  34. package/packages/datadog-instrumentations/src/vitest-util.js +254 -0
  35. package/packages/datadog-instrumentations/src/vitest-worker.js +823 -0
  36. package/packages/datadog-instrumentations/src/vitest.js +11 -1855
  37. package/packages/datadog-plugin-ai/src/index.js +1 -1
  38. package/packages/datadog-plugin-ai/src/tracing.js +66 -3
  39. package/packages/datadog-plugin-ai/src/utils.js +17 -4
  40. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/client.js +2 -2
  41. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/context.js +19 -10
  42. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/handler.js +1 -0
  43. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +47 -11
  44. package/packages/datadog-plugin-child_process/src/index.js +13 -2
  45. package/packages/datadog-plugin-claude-agent-sdk/src/index.js +31 -0
  46. package/packages/datadog-plugin-claude-agent-sdk/src/tracing.js +107 -0
  47. package/packages/datadog-plugin-claude-agent-sdk/src/util.js +15 -0
  48. package/packages/datadog-plugin-cucumber/src/index.js +15 -24
  49. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +360 -14
  50. package/packages/datadog-plugin-cypress/src/index.js +47 -2
  51. package/packages/datadog-plugin-cypress/src/plugin.js +1 -0
  52. package/packages/datadog-plugin-cypress/src/support.js +45 -1
  53. package/packages/datadog-plugin-express/src/code_origin.js +1 -1
  54. package/packages/datadog-plugin-fastify/src/code_origin.js +1 -1
  55. package/packages/datadog-plugin-graphql/src/execute.js +639 -12
  56. package/packages/datadog-plugin-graphql/src/index.js +37 -9
  57. package/packages/datadog-plugin-graphql/src/parse.js +24 -4
  58. package/packages/datadog-plugin-graphql/src/utils.js +9 -2
  59. package/packages/datadog-plugin-graphql/src/validate.js +17 -6
  60. package/packages/datadog-plugin-http/src/index.js +6 -8
  61. package/packages/datadog-plugin-jest/src/index.js +31 -15
  62. package/packages/datadog-plugin-mocha/src/index.js +40 -15
  63. package/packages/datadog-plugin-mongodb-core/src/bulk-write.js +43 -0
  64. package/packages/datadog-plugin-mongodb-core/src/index.js +8 -443
  65. package/packages/datadog-plugin-mongodb-core/src/query.js +452 -0
  66. package/packages/datadog-plugin-openai/src/services.js +2 -2
  67. package/packages/datadog-plugin-playwright/src/index.js +4 -3
  68. package/packages/datadog-plugin-vitest/src/index.js +120 -72
  69. package/packages/datadog-shimmer/src/shimmer.js +37 -16
  70. package/packages/dd-trace/src/aiguard/index.js +9 -14
  71. package/packages/dd-trace/src/aiguard/integrations/index.js +34 -0
  72. package/packages/dd-trace/src/aiguard/integrations/openai.js +47 -33
  73. package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +42 -28
  74. package/packages/dd-trace/src/aiguard/sdk.js +2 -2
  75. package/packages/dd-trace/src/appsec/api_security/sampler.js +3 -3
  76. package/packages/dd-trace/src/appsec/channels.js +3 -1
  77. package/packages/dd-trace/src/appsec/downstream_requests.js +4 -4
  78. package/packages/dd-trace/src/appsec/graphql.js +14 -11
  79. package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +23 -23
  80. package/packages/dd-trace/src/appsec/iast/security-controls/index.js +2 -2
  81. package/packages/dd-trace/src/appsec/iast/telemetry/index.js +2 -1
  82. package/packages/dd-trace/src/appsec/index.js +10 -1
  83. package/packages/dd-trace/src/appsec/lambda.js +135 -0
  84. package/packages/dd-trace/src/appsec/reporter.js +49 -10
  85. package/packages/dd-trace/src/appsec/telemetry/index.js +1 -1
  86. package/packages/dd-trace/src/appsec/waf/index.js +16 -4
  87. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +4 -4
  88. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +26 -1
  89. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +115 -24
  90. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +21 -6
  91. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +5 -2
  92. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +1 -1
  93. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +1 -1
  94. package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +6 -2
  95. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +1 -1
  96. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +130 -20
  97. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +2 -2
  98. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +17 -0
  99. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +47 -26
  100. package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +2 -2
  101. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +64 -53
  102. package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +3 -3
  103. package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +156 -0
  104. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +23 -5
  105. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +128 -0
  106. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +287 -0
  107. package/packages/dd-trace/src/config/defaults.js +3 -2
  108. package/packages/dd-trace/src/config/generated-config-types.d.ts +71 -57
  109. package/packages/dd-trace/src/config/helper.js +1 -0
  110. package/packages/dd-trace/src/config/index.js +35 -22
  111. package/packages/dd-trace/src/config/major-overrides.js +4 -2
  112. package/packages/dd-trace/src/config/remote_config.js +1 -1
  113. package/packages/dd-trace/src/config/supported-configurations.json +127 -56
  114. package/packages/dd-trace/src/constants.js +7 -0
  115. package/packages/dd-trace/src/crashtracking/crashtracker.js +2 -2
  116. package/packages/dd-trace/src/datastreams/processor.js +11 -3
  117. package/packages/dd-trace/src/exporters/agent/index.js +1 -1
  118. package/packages/dd-trace/src/exporters/agentless/writer.js +4 -4
  119. package/packages/dd-trace/src/llmobs/index.js +1 -1
  120. package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +403 -0
  121. package/packages/dd-trace/src/llmobs/plugins/ai/index.js +8 -395
  122. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +27 -0
  123. package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +363 -0
  124. package/packages/dd-trace/src/llmobs/plugins/base.js +4 -4
  125. package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/index.js +270 -0
  126. package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/utils.js +10 -0
  127. package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
  128. package/packages/dd-trace/src/llmobs/sdk.js +3 -3
  129. package/packages/dd-trace/src/llmobs/span_processor.js +1 -1
  130. package/packages/dd-trace/src/llmobs/tagger.js +3 -3
  131. package/packages/dd-trace/src/llmobs/writers/base.js +1 -1
  132. package/packages/dd-trace/src/opentelemetry/metrics/index.js +54 -5
  133. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +40 -0
  134. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +169 -0
  135. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +38 -39
  136. package/packages/dd-trace/src/opentelemetry/span.js +5 -0
  137. package/packages/dd-trace/src/opentelemetry/trace/index.js +4 -0
  138. package/packages/dd-trace/src/opentelemetry/tracer_provider.js +1 -1
  139. package/packages/dd-trace/src/opentracing/tracer.js +6 -1
  140. package/packages/dd-trace/src/plugin_manager.js +23 -7
  141. package/packages/dd-trace/src/plugins/ci_plugin.js +159 -10
  142. package/packages/dd-trace/src/plugins/index.js +2 -0
  143. package/packages/dd-trace/src/plugins/util/llm.js +6 -1
  144. package/packages/dd-trace/src/plugins/util/test.js +15 -9
  145. package/packages/dd-trace/src/profiling/exporter_cli.js +2 -2
  146. package/packages/dd-trace/src/profiling/exporters/agent.js +28 -3
  147. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +3 -3
  148. package/packages/dd-trace/src/profiling/profilers/wall.js +1 -1
  149. package/packages/dd-trace/src/proxy.js +8 -8
  150. package/packages/dd-trace/src/span_processor.js +5 -6
  151. package/packages/dd-trace/src/span_stats.js +96 -78
  152. package/packages/dd-trace/src/startup-log.js +2 -1
  153. package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
  154. package/packages/dd-trace/src/telemetry/endpoints.js +6 -4
  155. package/packages/dd-trace/src/telemetry/index.js +2 -2
  156. package/packages/dd-trace/src/telemetry/logs/index.js +1 -1
  157. package/packages/dd-trace/src/telemetry/send-data.js +8 -8
  158. package/packages/dd-trace/src/telemetry/session-propagation.js +1 -1
  159. package/packages/dd-trace/src/telemetry/telemetry.js +9 -9
  160. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  161. package/vendor/dist/protobufjs/index.js +1 -1
  162. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  163. package/vendor/dist/shell-quote/index.js +1 -1
  164. package/packages/datadog-plugin-graphql/src/resolve.js +0 -170
@@ -44,8 +44,6 @@
44
44
  "@oxc-project/types","https://github.com/oxc-project/oxc","['MIT']","['Boshen and oxc contributors']"
45
45
  "@tybys/wasm-util","https://github.com/toyobayashi/wasm-util","['MIT']","['toyobayashi']"
46
46
  "@types/estree","https://github.com/DefinitelyTyped/DefinitelyTyped","['MIT']","['DefinitelyTyped']"
47
- "acorn","https://github.com/acornjs/acorn","['MIT']","['acornjs']"
48
- "acorn-import-attributes","https://github.com/xtuc/acorn-import-attributes","['MIT']","['Sven Sauleau']"
49
47
  "argparse","https://github.com/nodeca/argparse","['Python-2.0']","['nodeca']"
50
48
  "astring","https://github.com/davidbonnet/astring","['MIT']","['David Bonnet']"
51
49
  "cjs-module-lexer","https://github.com/nodejs/cjs-module-lexer","['MIT']","['Guy Bedford']"
@@ -53,6 +51,7 @@
53
51
  "dc-polyfill","https://github.com/DataDog/dc-polyfill","['MIT']","['Thomas Hunter II']"
54
52
  "dd-trace","https://github.com/DataDog/dd-trace-js","['(Apache-2.0 OR BSD-3-Clause)']","['Datadog Inc. <info@datadoghq.com>']"
55
53
  "detect-newline","https://github.com/sindresorhus/detect-newline","['MIT']","['Sindre Sorhus']"
54
+ "es-module-lexer","https://github.com/guybedford/es-module-lexer","['MIT']","['Guy Bedford']"
56
55
  "escape-string-regexp","https://github.com/sindresorhus/escape-string-regexp","['MIT']","['Sindre Sorhus']"
57
56
  "esquery","https://github.com/estools/esquery","['BSD-3-Clause']","['Joel Feenstra']"
58
57
  "estraverse","https://github.com/estools/estraverse","['BSD-2-Clause']","['estools']"
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # `dd-trace`: Node.js APM Tracer Library
2
2
 
3
- [![npm v5](https://img.shields.io/npm/v/dd-trace/latest?color=blue&label=dd-trace%40v5&logo=npm)](https://www.npmjs.com/package/dd-trace)
3
+ [![npm v6](https://img.shields.io/npm/v/dd-trace/latest?color=blue&label=dd-trace%40v6&logo=npm)](https://www.npmjs.com/package/dd-trace)
4
4
  [![codecov](https://codecov.io/gh/DataDog/dd-trace-js/branch/master/graph/badge.svg)](https://codecov.io/gh/DataDog/dd-trace-js)
5
5
 
6
6
  <img align="right" src="https://user-images.githubusercontent.com/551402/208212084-1d0c07e2-4135-4c61-b2da-8f2fddbc66ed.png" alt="Bits the dog JavaScript" width="200px"/>
@@ -28,30 +28,35 @@ Most of the documentation for `dd-trace` is available on these webpages:
28
28
  | [`v2`](https://github.com/DataDog/dd-trace-js/tree/v2.x) | ![npm v2](https://img.shields.io/npm/v/dd-trace/latest-node12?color=white&label=%20&style=flat-square) | `>= v12` | NO | NO | **EOL** | 2022-01-28 | 2023-08-15 |
29
29
  | [`v3`](https://github.com/DataDog/dd-trace-js/tree/v3.x) | ![npm v3](https://img.shields.io/npm/v/dd-trace/latest-node14?color=white&label=%20&style=flat-square) | `>= v14` | NO | YES | **EOL** | 2022-08-15 | 2024-05-15 |
30
30
  | [`v4`](https://github.com/DataDog/dd-trace-js/tree/v4.x) | ![npm v4](https://img.shields.io/npm/v/dd-trace/latest-node16?color=white&label=%20&style=flat-square) | `>= v16` | YES | YES | **EOL** | 2023-05-12 | 2025-01-11 |
31
- | [`v5`](https://github.com/DataDog/dd-trace-js/tree/v5.x) | ![npm v5](https://img.shields.io/npm/v/dd-trace/latest?color=white&label=%20&style=flat-square) | `>= v18` | YES | YES | **Current** | 2024-01-11 | Unknown |
31
+ | [`v5`](https://github.com/DataDog/dd-trace-js/tree/v5.x) | ![npm v5](https://img.shields.io/npm/v/dd-trace/latest-node18?color=white&label=%20&style=flat-square) | `>= v18` | YES | YES | **Maintenance** | 2024-01-11 | Unknown |
32
+ | [`v6`](https://github.com/DataDog/dd-trace-js/tree/v6.x) | ![npm v6](https://img.shields.io/npm/v/dd-trace/latest?color=white&label=%20&style=flat-square) | `>= v22` | YES | YES | **Current** | 2026-07-02 | Unknown |
32
33
 
33
34
  * EOL = End-of-life
34
35
  * SSI = Single-Step Install
35
36
 
36
- We currently maintain one release line, namely `v5`.
37
+ We currently maintain two release lines: `v6` (current) and `v5` (maintenance).
37
38
 
38
- For any new projects it is recommended to use the `v5` release line:
39
+ For any new projects it is recommended to use the `v6` release line:
39
40
 
40
41
  ```sh
41
42
  $ npm install dd-trace
42
43
  $ yarn add dd-trace
43
44
  ```
44
45
 
45
- Existing projects that need to use EOL versions of Node.js may continue to use these older release lines.
46
- This is done by specifying the version when installing the package.
46
+ Existing projects on `v5` may continue to use it during its maintenance window by pinning the major version:
47
+
48
+ ```sh
49
+ $ npm install dd-trace@5
50
+ $ yarn add dd-trace@5
51
+ ```
52
+
53
+ Older EOL release lines are no longer maintained, but can still be pinned if needed:
47
54
 
48
55
  ```sh
49
56
  $ npm install dd-trace@4 # or whatever version you need
50
57
  $ yarn add dd-trace@4 # or whatever version you need
51
58
  ```
52
59
 
53
- Note, however, that the end-of-life release lines are no longer maintained and will not receive updates.
54
-
55
60
  Any backwards-breaking functionality that is introduced into the library will result in an increase of the major version of the library and therefore a new release line.
56
61
  Such releases are kept to a minimum to reduce the pain of upgrading the library.
57
62
 
package/ci/init.js CHANGED
@@ -1,13 +1,14 @@
1
1
  'use strict'
2
2
 
3
3
  /* eslint-disable no-console */
4
- const tracer = require('../packages/dd-trace')
5
4
  const log = require('../packages/dd-trace/src/log')
6
5
  const { getEnvironmentVariable, getValueFromEnvSources } = require('../packages/dd-trace/src/config/helper')
6
+ const { isTrue } = require('../packages/dd-trace/src/util')
7
7
 
8
8
  const PACKAGE_MANAGERS = ['npm', 'yarn', 'pnpm']
9
9
  const DEFAULT_FLUSH_INTERVAL = 5000
10
10
  const JEST_FLUSH_INTERVAL = 0
11
+ const VITEST_NO_WORKER_INIT_ACTIVE_ENV = 'DD_TEST_OPT_VITEST_NO_WORKER_INIT_ACTIVE'
11
12
  const EXPORTER_MAP = {
12
13
  jest: 'jest_worker',
13
14
  cucumber: 'cucumber_worker',
@@ -78,10 +79,28 @@ if (isTestWorker) {
78
79
  }
79
80
  }
80
81
 
81
- if (shouldInit) {
82
+ const skipVitestWorkerInit = shouldSkipVitestWorkerInit()
83
+ const tracer = skipVitestWorkerInit
84
+ ? {
85
+ init () {},
86
+ use () {},
87
+ }
88
+ : require('../packages/dd-trace')
89
+
90
+ if (shouldInit && !skipVitestWorkerInit) {
82
91
  tracer.init(baseOptions)
83
92
  tracer.use('fs', false)
84
93
  tracer.use('child_process', false)
85
94
  }
86
95
 
87
96
  module.exports = tracer
97
+
98
+ function shouldSkipVitestWorkerInit () {
99
+ return getValueFromEnvSources('DD_VITEST_WORKER') &&
100
+ isVitestNoWorkerInitActive()
101
+ }
102
+
103
+ function isVitestNoWorkerInitActive () {
104
+ // eslint-disable-next-line eslint-rules/eslint-process-env
105
+ return isTrue(process.env[VITEST_NO_WORKER_INIT_ACTIVE_ENV])
106
+ }
@@ -0,0 +1,430 @@
1
+ import { realpathSync } from 'node:fs'
2
+ import { relative } from 'node:path'
3
+ import { performance } from 'node:perf_hooks'
4
+
5
+ import { afterEach, beforeAll, beforeEach } from 'vitest'
6
+
7
+ // Instrumentation-less worker setup for DD_EXPERIMENTAL_TEST_OPT_VITEST_NO_WORKER_INIT.
8
+ // It applies Test Optimization execution changes without initializing dd-trace in Vitest workers.
9
+ const VITEST_NO_WORKER_INIT_ACTIVE_ENV = 'DD_TEST_OPT_VITEST_NO_WORKER_INIT_ACTIVE'
10
+ const isNoWorkerInitActive = getIsNoWorkerInitActive()
11
+ const providedContext = isNoWorkerInitActive ? getProvidedContext() : {}
12
+ const attemptToFixTests = providedContext.attemptToFixTests || {}
13
+ const attemptToFixRetries = providedContext.attemptToFixRetries || 0
14
+ const disabledTests = providedContext.disabledTests || {}
15
+ const earlyFlakeDetectionRetries = providedContext.earlyFlakeDetectionRetries || 0
16
+ const earlyFlakeDetectionRetryThresholds = Array.isArray(providedContext.earlyFlakeDetectionRetryThresholds)
17
+ ? providedContext.earlyFlakeDetectionRetryThresholds
18
+ : []
19
+ const earlyFlakeDetectionSlowRetries = providedContext.earlyFlakeDetectionSlowRetries || {}
20
+ const hasEarlyFlakeDetectionSlowRetries = Object.keys(earlyFlakeDetectionSlowRetries).length > 0
21
+ const isEarlyFlakeDetectionEnabled = providedContext.isEarlyFlakeDetectionEnabled === true
22
+ const knownTests = providedContext.knownTests || {}
23
+ const modifiedFiles = providedContext.modifiedFiles || {}
24
+ const quarantinedTests = providedContext.quarantinedTests || {}
25
+ const repositoryRoot = realpath(providedContext.repositoryRoot || process.cwd())
26
+ let setVitestTaskFn
27
+ if (isNoWorkerInitActive) {
28
+ try {
29
+ // Vitest does not expose setFn from the public setup API; keep this optional for strict installers.
30
+ const vitestRunner = await import('@vitest/runner')
31
+ setVitestTaskFn = vitestRunner.setFn
32
+ } catch {}
33
+ }
34
+ const earlyFlakeDetectionRetriesByTask = new WeakMap()
35
+ const earlyFlakeDetectionSkippedResults = new WeakMap()
36
+ const earlyFlakeDetectionStartByTask = new WeakMap()
37
+ const nextAttemptIndexByTask = new WeakMap()
38
+ const realpaths = new Map()
39
+
40
+ if (isNoWorkerInitActive) {
41
+ // eslint-disable-next-line no-empty-pattern
42
+ beforeAll(function ({}, suite) {
43
+ suite ||= arguments[0]
44
+ applyExecutionChanges(suite)
45
+ })
46
+
47
+ beforeEach(function ({ onTestFinished, task, skip }) {
48
+ const testSuite = getTestSuite(task)
49
+ const testName = getTestName(task)
50
+ const isAttemptToFixTest = attemptToFixTests[testSuite]?.[testName]
51
+ const isEarlyFlakeDetectionTestAttempt = isEarlyFlakeDetectionTest(testSuite, testName)
52
+ const isQuarantinedTest = quarantinedTests[testSuite]?.[testName] && !isAttemptToFixTest
53
+ const attemptIndex = getNextAttemptIndex(task)
54
+ if (attemptIndex > 0) {
55
+ recordTestOptimizationStatus(task, attemptIndex - 1)
56
+ }
57
+
58
+ if (disabledTests[testSuite]?.[testName] && !isAttemptToFixTest) {
59
+ skip('Skipped by Datadog Test Optimization')
60
+ } else if (isAttemptToFixTest && attemptIndex > 0) {
61
+ task.result.state = 'run'
62
+ } else if (isEarlyFlakeDetectionTestAttempt) {
63
+ const isSkippedRepeat = prepareEarlyFlakeDetectionAttempt(task, attemptIndex)
64
+ if (!isSkippedRepeat && attemptIndex > 0) {
65
+ task.result.state = 'run'
66
+ }
67
+ }
68
+
69
+ if (isAttemptToFixTest || isEarlyFlakeDetectionTestAttempt || isQuarantinedTest) {
70
+ onTestFinished(() => {
71
+ if (attemptIndex === getFinalAttemptIndex(task)) {
72
+ if (isAttemptToFixTest || isEarlyFlakeDetectionTestAttempt) {
73
+ recordTestOptimizationStatus(task, attemptIndex, true)
74
+ }
75
+ switchQuarantinedFinalFailure(task, attemptIndex)
76
+ }
77
+ })
78
+ }
79
+ })
80
+
81
+ afterEach(function ({ task }) {
82
+ const attemptIndex = task.meta.__ddTestOptCurrentAttemptIndex
83
+ if (restoreEarlyFlakeDetectionSkippedResult(task)) {
84
+ return
85
+ }
86
+ if (attemptIndex === getFinalAttemptIndex(task)) {
87
+ recordTestOptimizationStatus(task, attemptIndex)
88
+ }
89
+ switchQuarantinedFinalFailure(task, attemptIndex)
90
+ })
91
+ }
92
+
93
+ function applyExecutionChanges (suite) {
94
+ for (const task of suite?.tasks || []) {
95
+ if (task.type === 'suite') {
96
+ applyExecutionChanges(task)
97
+ continue
98
+ }
99
+
100
+ const testSuite = getTestSuite(task)
101
+ const testName = getTestName(task)
102
+ if (attemptToFixTests[testSuite]?.[testName]) {
103
+ task.retry = 0
104
+ task.repeats = attemptToFixRetries
105
+ task.meta.__ddTestOptAtfRetries = attemptToFixRetries
106
+ } else if (disabledTests[testSuite]?.[testName]) {
107
+ task.mode = 'skip'
108
+ } else if (isEarlyFlakeDetectionTest(testSuite, testName)) {
109
+ task.retry = 0
110
+ task.repeats = earlyFlakeDetectionRetries
111
+ task.meta.__ddTestOptEfdRetries = earlyFlakeDetectionRetries
112
+ }
113
+ }
114
+ }
115
+
116
+ function getNextAttemptIndex (task) {
117
+ const attemptIndex = nextAttemptIndexByTask.get(task) || 0
118
+ nextAttemptIndexByTask.set(task, attemptIndex + 1)
119
+ task.meta.__ddTestOptCurrentAttemptIndex = attemptIndex
120
+ return attemptIndex
121
+ }
122
+
123
+ function recordTestOptimizationStatus (task, attemptIndex = task.result?.repeatCount || 0, onlyIfNewErrors = false) {
124
+ const testSuite = getTestSuite(task)
125
+ const testName = getTestName(task)
126
+
127
+ if (attemptToFixTests[testSuite]?.[testName]) {
128
+ recordAttemptToFixStatus(task, attemptIndex, onlyIfNewErrors)
129
+ } else if (isEarlyFlakeDetectionTest(testSuite, testName)) {
130
+ recordEarlyFlakeDetectionStatus(task, attemptIndex, onlyIfNewErrors)
131
+ } else if (task.repeats > 0) {
132
+ recordManualRepeatStatus(task, attemptIndex)
133
+ }
134
+ }
135
+
136
+ function recordAttemptToFixStatus (task, attemptIndex, onlyIfNewErrors) {
137
+ if (onlyIfNewErrors && !hasNewErrors(task.meta.__ddTestOptAtfErrorCounts, attemptIndex, task)) {
138
+ return
139
+ }
140
+
141
+ task.meta.__ddTestOptAtfStatuses ||= []
142
+ task.meta.__ddTestOptAtfErrorCounts ||= []
143
+ task.meta.__ddTestOptAtfStatuses[attemptIndex] = getAttemptStatus(
144
+ task,
145
+ task.meta.__ddTestOptAtfErrorCounts,
146
+ attemptIndex
147
+ )
148
+ task.meta.__ddTestOptAtfErrorCounts[attemptIndex] = task.result?.errors?.length || 0
149
+
150
+ if (
151
+ attemptIndex === getAttemptToFixRetryCount(task) &&
152
+ task.meta.__ddTestOptAtfStatuses.includes('fail') &&
153
+ task.result?.state === 'pass'
154
+ ) {
155
+ task.result.state = 'fail'
156
+ }
157
+ }
158
+
159
+ function recordEarlyFlakeDetectionStatus (task, attemptIndex, onlyIfNewErrors) {
160
+ if (restoreEarlyFlakeDetectionSkippedResult(task)) {
161
+ return
162
+ }
163
+
164
+ const retryCount = earlyFlakeDetectionRetriesByTask.get(task)
165
+ if (retryCount !== undefined && attemptIndex > retryCount) {
166
+ return
167
+ }
168
+
169
+ if (onlyIfNewErrors && !hasNewErrors(task.meta.__ddTestOptEfdErrorCounts, attemptIndex, task)) {
170
+ return
171
+ }
172
+
173
+ if (!earlyFlakeDetectionRetriesByTask.has(task)) {
174
+ const retryCount = getEarlyFlakeDetectionRetryCount(task)
175
+ earlyFlakeDetectionRetriesByTask.set(task, retryCount)
176
+ task.repeats = retryCount
177
+ task.meta.__ddTestOptEfdRetries = retryCount
178
+ if (retryCount === 0 && hasEarlyFlakeDetectionSlowRetries) {
179
+ task.meta.__ddTestOptEfdAbortReason = 'slow'
180
+ }
181
+ }
182
+
183
+ task.meta.__ddTestOptEfdStatuses ||= []
184
+ task.meta.__ddTestOptEfdErrorCounts ||= []
185
+ task.meta.__ddTestOptEfdStatuses[attemptIndex] = getAttemptStatus(
186
+ task,
187
+ task.meta.__ddTestOptEfdErrorCounts,
188
+ attemptIndex
189
+ )
190
+ task.meta.__ddTestOptEfdErrorCounts[attemptIndex] = task.result?.errors?.length || 0
191
+
192
+ if (attemptIndex === getEarlyFlakeDetectionRetryCountForTask(task) &&
193
+ task.meta.__ddTestOptEfdStatuses.includes('pass')) {
194
+ task.result.state = 'pass'
195
+ }
196
+ }
197
+
198
+ function recordManualRepeatStatus (task, attemptIndex) {
199
+ task.meta.__ddTestOptRepeatStatuses ||= []
200
+ task.meta.__ddTestOptRepeatErrorCounts ||= []
201
+ task.meta.__ddTestOptRepeatStatuses[attemptIndex] = getManualRepeatStatus(
202
+ task,
203
+ task.meta.__ddTestOptRepeatErrorCounts,
204
+ attemptIndex
205
+ )
206
+ task.meta.__ddTestOptRepeatErrorCounts[attemptIndex] = task.result?.errors?.length || 0
207
+ }
208
+
209
+ function restoreEarlyFlakeDetectionSkippedResult (task) {
210
+ if (!task.meta.__ddTestOptEfdSkipCurrentAttempt) {
211
+ return false
212
+ }
213
+
214
+ delete task.meta.__ddTestOptEfdSkipCurrentAttempt
215
+ const skippedResult = earlyFlakeDetectionSkippedResults.get(task)
216
+ if (skippedResult) {
217
+ task.result = skippedResult
218
+ earlyFlakeDetectionSkippedResults.delete(task)
219
+ }
220
+ return true
221
+ }
222
+
223
+ function hasNewErrors (errorCounts, attemptIndex, task) {
224
+ const recordedErrorCount = errorCounts?.[attemptIndex]
225
+ const previousErrorCount = recordedErrorCount ?? getPreviousErrorCount(errorCounts, attemptIndex)
226
+ return (task.result?.errors?.length || 0) > previousErrorCount
227
+ }
228
+
229
+ function getFinalAttemptIndex (task) {
230
+ const testSuite = getTestSuite(task)
231
+ const testName = getTestName(task)
232
+
233
+ if (attemptToFixTests[testSuite]?.[testName]) {
234
+ return getAttemptToFixRetryCount(task)
235
+ }
236
+ if (isEarlyFlakeDetectionTest(testSuite, testName)) {
237
+ return getEarlyFlakeDetectionRetryCountForTask(task)
238
+ }
239
+ return task.repeats
240
+ }
241
+
242
+ function switchQuarantinedFinalFailure (task, attemptIndex) {
243
+ const testSuite = getTestSuite(task)
244
+ const testName = getTestName(task)
245
+ if (
246
+ !quarantinedTests[testSuite]?.[testName] ||
247
+ attemptToFixTests[testSuite]?.[testName] ||
248
+ task.result?.state !== 'fail'
249
+ ) {
250
+ return
251
+ }
252
+
253
+ const retryCount = task.result?.retryCount || 0
254
+ const retryLimit = task.retry || 0
255
+ if (retryCount < retryLimit || attemptIndex < getFinalAttemptIndex(task)) {
256
+ return
257
+ }
258
+
259
+ task.meta.__ddTestOptQuarantinedFailed = true
260
+ task.result.state = 'pass'
261
+ }
262
+
263
+ function getAttemptToFixRetryCount (task) {
264
+ return task.meta.__ddTestOptAtfRetries ?? task.repeats
265
+ }
266
+
267
+ function getEarlyFlakeDetectionRetryCountForTask (task) {
268
+ return earlyFlakeDetectionRetriesByTask.get(task) ?? task.meta.__ddTestOptEfdRetries ?? task.repeats
269
+ }
270
+
271
+ function getAttemptStatus (task, errorCounts, repeatCount) {
272
+ const errorCount = task.result?.errors?.length || 0
273
+ if (errorCount > getPreviousErrorCount(errorCounts, repeatCount)) {
274
+ return 'fail'
275
+ }
276
+ return task.result?.state === 'fail' ? 'fail' : 'pass'
277
+ }
278
+
279
+ function getManualRepeatStatus (task, errorCounts, repeatCount) {
280
+ const errorCount = task.result?.errors?.length || 0
281
+ return errorCount > getPreviousErrorCount(errorCounts, repeatCount) ? 'fail' : 'pass'
282
+ }
283
+
284
+ function getPreviousErrorCount (errorCounts, repeatCount) {
285
+ for (let index = repeatCount - 1; index >= 0; index--) {
286
+ if (errorCounts[index] !== undefined) {
287
+ return errorCounts[index]
288
+ }
289
+ }
290
+ return 0
291
+ }
292
+
293
+ function prepareEarlyFlakeDetectionAttempt (task, attemptIndex) {
294
+ if (attemptIndex === 0) {
295
+ earlyFlakeDetectionStartByTask.set(task, performance.now())
296
+ return false
297
+ }
298
+
299
+ let retryCount = earlyFlakeDetectionRetriesByTask.get(task)
300
+ if (retryCount === undefined) {
301
+ retryCount = getEarlyFlakeDetectionRetryCount(task)
302
+ earlyFlakeDetectionRetriesByTask.set(task, retryCount)
303
+ task.repeats = retryCount
304
+ task.meta.__ddTestOptEfdRetries = retryCount
305
+ if (retryCount === 0 && hasEarlyFlakeDetectionSlowRetries) {
306
+ task.meta.__ddTestOptEfdAbortReason = 'slow'
307
+ }
308
+ }
309
+
310
+ if (attemptIndex <= retryCount) {
311
+ earlyFlakeDetectionStartByTask.set(task, performance.now())
312
+ return false
313
+ }
314
+
315
+ if (!canReplaceVitestTaskFn()) {
316
+ earlyFlakeDetectionStartByTask.set(task, performance.now())
317
+ return false
318
+ }
319
+
320
+ if (task.result) {
321
+ earlyFlakeDetectionSkippedResults.set(task, {
322
+ ...task.result,
323
+ errors: task.result.errors?.slice(),
324
+ })
325
+ }
326
+ task.meta.__ddTestOptEfdSkipCurrentAttempt = true
327
+ replaceVitestTaskFn(task, noopTest)
328
+ return true
329
+ }
330
+
331
+ function getEarlyFlakeDetectionRetryCount (task) {
332
+ if (!hasEarlyFlakeDetectionSlowRetries) {
333
+ return earlyFlakeDetectionRetries
334
+ }
335
+
336
+ const executionStart = earlyFlakeDetectionStartByTask.get(task)
337
+ const duration = executionStart === undefined ? task.result?.duration ?? 0 : performance.now() - executionStart
338
+ for (const { key, limitMs } of earlyFlakeDetectionRetryThresholds) {
339
+ if (duration < limitMs) {
340
+ return earlyFlakeDetectionSlowRetries[key] ?? 0
341
+ }
342
+ }
343
+ return 0
344
+ }
345
+
346
+ function noopTest () {}
347
+
348
+ /**
349
+ * Returns whether Vitest's private task function setter is available.
350
+ *
351
+ * @returns {boolean}
352
+ */
353
+ function canReplaceVitestTaskFn () {
354
+ return typeof setVitestTaskFn === 'function'
355
+ }
356
+
357
+ /**
358
+ * Replaces the function Vitest runs for a task.
359
+ *
360
+ * @param {object} task
361
+ * @param {(...args: unknown[]) => unknown} testFn
362
+ */
363
+ function replaceVitestTaskFn (task, testFn) {
364
+ setVitestTaskFn(task, testFn)
365
+ }
366
+
367
+ function isEarlyFlakeDetectionTest (testSuite, testName) {
368
+ if (!isEarlyFlakeDetectionEnabled || earlyFlakeDetectionRetries <= 0) return false
369
+ if (isModifiedTest(testSuite)) return true
370
+ const testsForSuite = knownTests[testSuite] || []
371
+ return !testsForSuite.includes(testName)
372
+ }
373
+
374
+ function isModifiedTest (testSuite) {
375
+ return modifiedFiles[testSuite]?.length > 0
376
+ }
377
+
378
+ function getTestSuite (task) {
379
+ let filepath = realpaths.get(task.file.filepath)
380
+ if (filepath === undefined) {
381
+ filepath = realpath(task.file.filepath)
382
+ realpaths.set(task.file.filepath, filepath)
383
+ }
384
+
385
+ return normalizePath(relative(repositoryRoot, filepath))
386
+ }
387
+
388
+ function realpath (filepath) {
389
+ try {
390
+ return realpathSync(filepath)
391
+ } catch {
392
+ return filepath
393
+ }
394
+ }
395
+
396
+ function getTestName (task) {
397
+ let testName = task.name
398
+ let currentTask = task.suite
399
+
400
+ while (currentTask) {
401
+ if (currentTask.name) {
402
+ testName = `${currentTask.name} ${testName}`
403
+ }
404
+ currentTask = currentTask.suite
405
+ }
406
+
407
+ return testName
408
+ }
409
+
410
+ function normalizePath (filepath) {
411
+ return filepath.replaceAll('\\', '/')
412
+ }
413
+
414
+ function getIsNoWorkerInitActive () {
415
+ try {
416
+ // eslint-disable-next-line eslint-rules/eslint-process-env
417
+ const value = process.env[VITEST_NO_WORKER_INIT_ACTIVE_ENV]
418
+ return value === '1' || value === 'true'
419
+ } catch {
420
+ return false
421
+ }
422
+ }
423
+
424
+ function getProvidedContext () {
425
+ try {
426
+ return globalThis.__vitest_worker__.providedContext._ddVitestWorkerSetup || {}
427
+ } catch {
428
+ return {}
429
+ }
430
+ }
package/ext/tags.js CHANGED
@@ -27,6 +27,8 @@ const tags = {
27
27
  HTTP_CLIENT_IP: 'http.client_ip',
28
28
  NETWORK_CLIENT_IP: 'network.client.ip',
29
29
 
30
+ GRPC_STATUS_CODE: 'grpc.status.code',
31
+
30
32
  // Messaging
31
33
 
32
34
  // DSM Specific
package/index.d.ts CHANGED
@@ -233,6 +233,7 @@ interface Plugins {
233
233
  "amqp10": tracer.plugins.amqp10;
234
234
  "amqplib": tracer.plugins.amqplib;
235
235
  "anthropic": tracer.plugins.anthropic;
236
+ "claude-agent-sdk": tracer.plugins.claude_agent_sdk;
236
237
  "apollo": tracer.plugins.apollo;
237
238
  "avsc": tracer.plugins.avsc;
238
239
  "aws-durable-execution-sdk-js": tracer.plugins.aws_durable_execution_sdk_js;
@@ -2313,6 +2314,12 @@ declare namespace tracer {
2313
2314
  */
2314
2315
  interface amqplib extends Instrumentation {}
2315
2316
 
2317
+ /**
2318
+ * This plugin automatically instruments the
2319
+ * [@anthropic-ai/claude-agent-sdk](https://www.npmjs.com/package/@anthropic-ai/claude-agent-sdk) module.
2320
+ */
2321
+ interface claude_agent_sdk extends Instrumentation {}
2322
+
2316
2323
  /**
2317
2324
  * This plugin automatically instruments the
2318
2325
  * [anthropic](https://www.npmjs.com/package/@anthropic-ai/sdk) module.
@@ -2625,6 +2632,18 @@ declare namespace tracer {
2625
2632
  typeResolver?: any,
2626
2633
  }
2627
2634
 
2635
+ /** Context object passed to the `hooks.resolve` callback for each instrumented field. */
2636
+ interface FieldContext {
2637
+ /** The field name being resolved */
2638
+ fieldName: string;
2639
+ /** The dot-separated field path (e.g. `'user.address.city'`) */
2640
+ path: string;
2641
+ /** The error from the resolver, or `null` if it succeeded */
2642
+ error: Error | null;
2643
+ /** The value returned by the resolver (sync resolvers only; `undefined` for async) */
2644
+ result: unknown;
2645
+ }
2646
+
2628
2647
  /**
2629
2648
  * This plugin automatically instruments the
2630
2649
  * [graphql](https://github.com/graphql/graphql-js) module.
@@ -2655,6 +2674,7 @@ declare namespace tracer {
2655
2674
  * instrument the operation or to `-1` to instrument all fields/resolvers.
2656
2675
  *
2657
2676
  * @default -1
2677
+ * @env DD_TRACE_GRAPHQL_DEPTH
2658
2678
  */
2659
2679
  depth?: number;
2660
2680
 
@@ -2671,7 +2691,10 @@ declare namespace tracer {
2671
2691
  /**
2672
2692
  * An array of variable names to record. Can also be a callback that returns
2673
2693
  * the key/value pairs to record. For example, using
2674
- * `variables => variables` would record all variables.
2694
+ * `variables => variables` would record all variables. The environment
2695
+ * variable only accepts the array form (comma-separated variable names).
2696
+ *
2697
+ * @env DD_TRACE_GRAPHQL_VARIABLES
2675
2698
  */
2676
2699
  variables?: string[] | ((variables: { [key: string]: any }) => { [key: string]: any });
2677
2700
 
@@ -2680,9 +2703,18 @@ declare namespace tracer {
2680
2703
  * `users.*.name` span instead of `users.0.name`, `users.1.name`, etc)
2681
2704
  *
2682
2705
  * @default true
2706
+ * @env DD_TRACE_GRAPHQL_COLLAPSE
2683
2707
  */
2684
2708
  collapse?: boolean;
2685
2709
 
2710
+ /**
2711
+ * An array of error `extensions` keys to attach to the span error event
2712
+ * for each GraphQL error.
2713
+ *
2714
+ * @env DD_TRACE_GRAPHQL_ERROR_EXTENSIONS
2715
+ */
2716
+ errorExtensions?: string[];
2717
+
2686
2718
  /**
2687
2719
  * Whether to enable signature calculation for the resource name. This can
2688
2720
  * be disabled if your GraphQL operations always have a name. Note that when
@@ -2703,6 +2735,7 @@ declare namespace tracer {
2703
2735
  execute?: (span?: Span, args?: ExecutionArgs, res?: any) => void;
2704
2736
  validate?: (span?: Span, document?: any, errors?: any) => void;
2705
2737
  parse?: (span?: Span, source?: any, document?: any) => void;
2738
+ resolve?: (span?: Span, field?: FieldContext) => void;
2706
2739
  }
2707
2740
  }
2708
2741
 
package/initialize.mjs CHANGED
@@ -19,7 +19,7 @@ import { isMainThread } from 'worker_threads'
19
19
  // This file must support Node.js 12.0.0 syntax
20
20
 
21
21
  import {
22
- iitmExclusions,
22
+ iitmExclusionRegExp,
23
23
  load as hookLoad,
24
24
  resolve as hookResolve,
25
25
  } from './loader-hook.mjs'
@@ -67,8 +67,7 @@ const [NODE_MAJOR, NODE_MINOR] = process.versions.node.split('.').map(Number)
67
67
  const brokenLoaders = NODE_MAJOR === 18 && NODE_MINOR === 0
68
68
 
69
69
  export async function load (url, context, nextLoad) {
70
- const iitmExclusionsMatch = iitmExclusions.some((exclusion) => exclusion.test(url))
71
- const loadHook = (brokenLoaders || iitmExclusionsMatch) ? nextLoad : hookLoad
70
+ const loadHook = (brokenLoaders || iitmExclusionRegExp.test(url)) ? nextLoad : hookLoad
72
71
  return insertInit(await loadHook(url, context, nextLoad), url, context)
73
72
  }
74
73
 
@@ -78,8 +77,8 @@ if (isMainThread) {
78
77
  const require = Module.createRequire(import.meta.url)
79
78
  require('./init.js')
80
79
  if (Module.register) {
81
- Module.register('./loader-hook.mjs', import.meta.url, {
82
- data: { exclude: iitmExclusions },
83
- })
80
+ // The loader builds its own include/exclude matcher in `initialize`, so no
81
+ // options need to cross the registration boundary.
82
+ Module.register('./loader-hook.mjs', import.meta.url)
84
83
  }
85
84
  }