dd-trace 5.108.0 → 5.110.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 (174) hide show
  1. package/ci/init.js +4 -3
  2. package/index.d.ts +88 -25
  3. package/loader-hook.mjs +88 -2
  4. package/package.json +19 -14
  5. package/packages/datadog-esbuild/index.js +26 -0
  6. package/packages/datadog-esbuild/src/utils.js +46 -3
  7. package/packages/datadog-instrumentations/src/ai.js +43 -48
  8. package/packages/datadog-instrumentations/src/aws-durable-execution-sdk-js-context-methods.js +18 -0
  9. package/packages/datadog-instrumentations/src/aws-durable-execution-sdk-js.js +111 -0
  10. package/packages/datadog-instrumentations/src/aws-sdk.js +3 -1
  11. package/packages/datadog-instrumentations/src/connect.js +4 -3
  12. package/packages/datadog-instrumentations/src/electron.js +1 -1
  13. package/packages/datadog-instrumentations/src/fastify.js +4 -12
  14. package/packages/datadog-instrumentations/src/graphql.js +17 -6
  15. package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +81 -8
  16. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  17. package/packages/datadog-instrumentations/src/helpers/instrument.js +27 -2
  18. package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +65 -0
  19. package/packages/datadog-instrumentations/src/helpers/register.js +6 -4
  20. package/packages/datadog-instrumentations/src/helpers/require-optional-peer.js +17 -0
  21. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/aws-durable-execution-sdk-js.js +31 -0
  22. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
  23. package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +18 -2
  24. package/packages/datadog-instrumentations/src/http/client.js +12 -2
  25. package/packages/datadog-instrumentations/src/ioredis.js +0 -1
  26. package/packages/datadog-instrumentations/src/iovalkey.js +1 -2
  27. package/packages/datadog-instrumentations/src/koa.js +3 -2
  28. package/packages/datadog-instrumentations/src/mariadb.js +4 -2
  29. package/packages/datadog-instrumentations/src/mocha/main.js +3 -3
  30. package/packages/datadog-instrumentations/src/next.js +44 -10
  31. package/packages/datadog-instrumentations/src/openai.js +77 -18
  32. package/packages/datadog-instrumentations/src/otel-sdk-trace.js +7 -6
  33. package/packages/datadog-instrumentations/src/playwright.js +1 -1
  34. package/packages/datadog-instrumentations/src/redis.js +0 -1
  35. package/packages/datadog-instrumentations/src/restify.js +4 -3
  36. package/packages/datadog-instrumentations/src/router.js +18 -5
  37. package/packages/datadog-instrumentations/src/selenium.js +1 -2
  38. package/packages/datadog-instrumentations/src/tedious.js +28 -0
  39. package/packages/datadog-instrumentations/src/vitest.js +323 -7
  40. package/packages/datadog-plugin-avsc/src/schema_iterator.js +2 -2
  41. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/checkpoint.js +31 -0
  42. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/client.js +55 -0
  43. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/context.js +114 -0
  44. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/handler.js +128 -0
  45. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/index.js +19 -0
  46. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/trace-checkpoint.js +224 -0
  47. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +43 -0
  48. package/packages/datadog-plugin-aws-sdk/src/base.js +14 -16
  49. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +43 -6
  50. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +100 -37
  51. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +108 -99
  52. package/packages/datadog-plugin-bullmq/src/filter.js +35 -0
  53. package/packages/datadog-plugin-bullmq/src/producer.js +84 -4
  54. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +9 -1
  55. package/packages/datadog-plugin-fs/src/index.js +1 -0
  56. package/packages/datadog-plugin-http/src/client.js +8 -3
  57. package/packages/datadog-plugin-http2/src/client.js +5 -2
  58. package/packages/datadog-plugin-mysql/src/index.js +7 -2
  59. package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +2 -2
  60. package/packages/datadog-plugin-redis/src/index.js +1 -2
  61. package/packages/datadog-plugin-undici/src/index.js +5 -2
  62. package/packages/datadog-plugin-vitest/src/index.js +11 -2
  63. package/packages/datadog-webpack/index.js +17 -1
  64. package/packages/datadog-webpack/src/optional-peer-loader.js +17 -0
  65. package/packages/dd-trace/src/aiguard/channels.js +0 -1
  66. package/packages/dd-trace/src/aiguard/index.js +11 -49
  67. package/packages/dd-trace/src/aiguard/integrations/evaluate.js +46 -0
  68. package/packages/dd-trace/src/aiguard/integrations/openai.js +66 -0
  69. package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +78 -0
  70. package/packages/{datadog-instrumentations/src/helpers/ai-messages.js → dd-trace/src/aiguard/messages/openai.js} +85 -193
  71. package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +185 -0
  72. package/packages/dd-trace/src/aiguard/sdk.js +13 -1
  73. package/packages/dd-trace/src/appsec/api_security/index.js +55 -0
  74. package/packages/dd-trace/src/appsec/api_security/sampler.js +147 -0
  75. package/packages/dd-trace/src/appsec/channels.js +1 -0
  76. package/packages/dd-trace/src/appsec/downstream_requests.js +111 -58
  77. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/ldap-sensitive-analyzer.js +54 -12
  78. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +5 -1
  79. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +29 -4
  80. package/packages/dd-trace/src/appsec/index.js +10 -6
  81. package/packages/dd-trace/src/appsec/rasp/ssrf.js +19 -11
  82. package/packages/dd-trace/src/appsec/reporter.js +1 -0
  83. package/packages/dd-trace/src/appsec/telemetry/api_security.js +34 -0
  84. package/packages/dd-trace/src/appsec/telemetry/index.js +26 -0
  85. package/packages/dd-trace/src/ci-visibility/requests/request.js +1 -17
  86. package/packages/dd-trace/src/config/defaults.js +22 -3
  87. package/packages/dd-trace/src/config/generated-config-types.d.ts +538 -1
  88. package/packages/dd-trace/src/config/helper.js +133 -33
  89. package/packages/dd-trace/src/config/index.js +4 -1
  90. package/packages/dd-trace/src/config/parsers.js +19 -1
  91. package/packages/dd-trace/src/config/supported-configurations.json +45 -5
  92. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +49 -35
  93. package/packages/dd-trace/src/debugger/devtools_client/index.js +31 -66
  94. package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +83 -0
  95. package/packages/dd-trace/src/debugger/devtools_client/state.js +1 -0
  96. package/packages/dd-trace/src/debugger/index.js +5 -0
  97. package/packages/dd-trace/src/debugger/probe_sampler.js +112 -0
  98. package/packages/dd-trace/src/debugger/probe_sampler_constants.js +19 -0
  99. package/packages/dd-trace/src/dogstatsd.js +15 -8
  100. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +77 -26
  101. package/packages/dd-trace/src/exporters/agentless/index.js +7 -5
  102. package/packages/dd-trace/src/exporters/agentless/intake.js +43 -0
  103. package/packages/dd-trace/src/exporters/agentless/writer.js +5 -4
  104. package/packages/dd-trace/src/exporters/common/request.js +1 -20
  105. package/packages/dd-trace/src/exporters/common/url.js +34 -0
  106. package/packages/dd-trace/src/exporters/span-stats/writer.js +1 -3
  107. package/packages/dd-trace/src/guardrails/index.js +8 -5
  108. package/packages/dd-trace/src/index.js +6 -4
  109. package/packages/dd-trace/src/llmobs/constants/tags.js +6 -0
  110. package/packages/dd-trace/src/llmobs/index.js +16 -5
  111. package/packages/dd-trace/src/llmobs/plugins/genai/util.js +3 -3
  112. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +5 -1
  113. package/packages/dd-trace/src/llmobs/sdk.js +5 -3
  114. package/packages/dd-trace/src/llmobs/span_processor.js +4 -0
  115. package/packages/dd-trace/src/llmobs/tagger.js +55 -0
  116. package/packages/dd-trace/src/llmobs/telemetry.js +1 -0
  117. package/packages/dd-trace/src/log/index.js +18 -20
  118. package/packages/dd-trace/src/openfeature/flagging_provider.js +3 -1
  119. package/packages/dd-trace/src/opentelemetry/bridge-span-base.js +5 -4
  120. package/packages/dd-trace/src/opentelemetry/metrics/instruments.js +2 -1
  121. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +4 -3
  122. package/packages/dd-trace/src/opentelemetry/metrics/time.js +19 -0
  123. package/packages/dd-trace/src/opentelemetry/otlp/common.proto +2 -2
  124. package/packages/dd-trace/src/opentelemetry/otlp/logs.proto +1 -1
  125. package/packages/dd-trace/src/opentelemetry/otlp/logs_service.proto +1 -1
  126. package/packages/dd-trace/src/opentelemetry/otlp/metrics.proto +1 -1
  127. package/packages/dd-trace/src/opentelemetry/otlp/metrics_service.proto +1 -1
  128. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +11 -4
  129. package/packages/dd-trace/src/opentelemetry/span-helpers.js +29 -13
  130. package/packages/dd-trace/src/opentelemetry/span.js +14 -3
  131. package/packages/dd-trace/src/opentelemetry/span_context.js +1 -0
  132. package/packages/dd-trace/src/opentelemetry/trace/index.js +2 -1
  133. package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +3 -2
  134. package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +26 -13
  135. package/packages/dd-trace/src/opentracing/propagation/text_map.js +3 -3
  136. package/packages/dd-trace/src/opentracing/span_context.js +17 -0
  137. package/packages/dd-trace/src/plugin_manager.js +7 -1
  138. package/packages/dd-trace/src/plugins/ci_plugin.js +36 -3
  139. package/packages/dd-trace/src/plugins/index.js +3 -0
  140. package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +276 -0
  141. package/packages/dd-trace/src/plugins/util/test.js +80 -0
  142. package/packages/dd-trace/src/plugins/util/url.js +68 -1
  143. package/packages/dd-trace/src/plugins/util/web.js +19 -36
  144. package/packages/dd-trace/src/priority_sampler.js +1 -13
  145. package/packages/dd-trace/src/profiling/config.js +129 -198
  146. package/packages/dd-trace/src/profiling/constants.js +4 -7
  147. package/packages/dd-trace/src/profiling/exporter_cli.js +10 -10
  148. package/packages/dd-trace/src/profiling/exporters/agent.js +23 -16
  149. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +45 -14
  150. package/packages/dd-trace/src/profiling/exporters/file.js +16 -5
  151. package/packages/dd-trace/src/profiling/oom.js +71 -0
  152. package/packages/dd-trace/src/profiling/profiler.js +35 -23
  153. package/packages/dd-trace/src/profiling/profilers/events.js +14 -7
  154. package/packages/dd-trace/src/profiling/profilers/space.js +27 -18
  155. package/packages/dd-trace/src/profiling/profilers/wall.js +35 -25
  156. package/packages/dd-trace/src/ritm.js +4 -2
  157. package/packages/dd-trace/src/serverless.js +1 -2
  158. package/packages/dd-trace/src/service-naming/schemas/util.js +36 -5
  159. package/packages/dd-trace/src/service-naming/schemas/v0/serverless.js +12 -0
  160. package/packages/dd-trace/src/service-naming/schemas/v1/serverless.js +12 -0
  161. package/packages/dd-trace/src/service-naming/schemas/v1/web.js +5 -3
  162. package/packages/dd-trace/src/span_format.js +2 -1
  163. package/packages/dd-trace/src/span_processor.js +6 -0
  164. package/packages/dd-trace/src/telemetry/send-data.js +12 -10
  165. package/packages/dd-trace/src/tracer.js +15 -1
  166. package/packages/dd-trace/src/util.js +14 -0
  167. package/register.js +58 -1
  168. package/vendor/dist/@apm-js-collab/code-transformer/index.js +1 -1
  169. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  170. package/vendor/dist/pprof-format/index.js +1 -1
  171. package/vendor/dist/protobufjs/index.js +1 -1
  172. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  173. package/packages/datadog-instrumentations/src/helpers/openai-ai-guard.js +0 -284
  174. package/packages/dd-trace/src/appsec/api_security_sampler.js +0 -117
@@ -2,7 +2,7 @@
2
2
 
3
3
  const METHODS = [...require('http').METHODS.map(v => v.toLowerCase()), 'all']
4
4
  const shimmer = require('../../datadog-shimmer')
5
- const { addHook, channel } = require('./helpers/instrument')
5
+ const { addHook, channel, createErrorPublisher } = require('./helpers/instrument')
6
6
  const { getCompileToRegexp } = require('./path-to-regexp')
7
7
 
8
8
  const {
@@ -40,6 +40,8 @@ function createWrapRouterMethod (name, compile) {
40
40
  const errorChannel = channel(`apm:${name}:middleware:error`)
41
41
  const nextChannel = channel(`apm:${name}:middleware:next`)
42
42
  const routeAddedChannel = channel(`apm:${name}:route:added`)
43
+ // Bound per name so express and a bare router keep independent guards.
44
+ const publishError = createErrorPublisher(errorChannel)
43
45
 
44
46
  function wrapLayerHandle (layer, original, matchers) {
45
47
  // Resolve `name` once at wrap time: cached on the original for any code
@@ -65,9 +67,20 @@ function createWrapRouterMethod (name, compile) {
65
67
  // `Layer.handleError`. Specialising lets the per-call body use named
66
68
  // parameters and `.call`, avoiding the rest-spread Array allocation that
67
69
  // the unified shape forced on every middleware invocation.
68
- return original.length === 4
70
+ const wrapped = original.length === 4
69
71
  ? shimmer.wrapFunction(original, errorHandlerLayerWrap(layer, name, captureRoute, needMultiMatch, matchers))
70
72
  : shimmer.wrapFunction(original, requestHandlerLayerWrap(layer, name, captureRoute, needMultiMatch, matchers))
73
+
74
+ // Workaround for loopback's phase-based middleware sorting. Its
75
+ // `_findLayerByHandler` maps a layer back to the user handler by scanning
76
+ // the layer handle's enumerable properties for the original function.
77
+ // Replacing `layer.handle` with this wrapper hides that handler, so without
78
+ // the back-reference loopback cannot tag the layer with its phase and
79
+ // `app.middleware(phase, ...)` handlers run in insertion order instead of
80
+ // phase order. The property name is part of that contract; keep it stable.
81
+ wrapped._datadog_orig = original
82
+
83
+ return wrapped
71
84
  }
72
85
 
73
86
  function requestHandlerLayerWrap (layer, name, captureRoute, needMultiMatch, matchers) {
@@ -91,7 +104,7 @@ function createWrapRouterMethod (name, compile) {
91
104
  try {
92
105
  return original.call(this, req, res, wrappedNext)
93
106
  } catch (error) {
94
- errorChannel.publish({ req, error })
107
+ publishError({ req, error })
95
108
  nextChannel.publish({ req })
96
109
  finishChannel.publish({ req })
97
110
 
@@ -123,7 +136,7 @@ function createWrapRouterMethod (name, compile) {
123
136
  try {
124
137
  return original.call(this, error, req, res, wrappedNext)
125
138
  } catch (caught) {
126
- errorChannel.publish({ req, error: caught })
139
+ publishError({ req, error: caught })
127
140
  nextChannel.publish({ req })
128
141
  finishChannel.publish({ req })
129
142
 
@@ -161,7 +174,7 @@ function createWrapRouterMethod (name, compile) {
161
174
  // router continuation so wrapCallback skips its name/length rewrite.
162
175
  return shimmer.wrapCallback(originalNext, original => function next (error) {
163
176
  if (error && error !== 'route' && error !== 'router') {
164
- errorChannel.publish({ req, error })
177
+ publishError({ req, error })
165
178
  }
166
179
 
167
180
  nextChannel.publish({ req })
@@ -19,8 +19,7 @@ if (window.DD_RUM && window.DD_RUM.stopSession) {
19
19
  `
20
20
  const IS_RUM_ACTIVE_SCRIPT = 'return !!window.DD_RUM'
21
21
 
22
- const DD_CIVISIBILITY_RUM_FLUSH_WAIT_MILLIS =
23
- Number(getValueFromEnvSources('DD_CIVISIBILITY_RUM_FLUSH_WAIT_MILLIS')) || 500
22
+ const DD_CIVISIBILITY_RUM_FLUSH_WAIT_MILLIS = getValueFromEnvSources('DD_CIVISIBILITY_RUM_FLUSH_WAIT_MILLIS')
24
23
  const DD_CIVISIBILITY_TEST_EXECUTION_ID_COOKIE_NAME = 'datadog-ci-visibility-test-execution-id'
25
24
 
26
25
  // TODO: can we increase the supported version range?
@@ -4,12 +4,40 @@ const shimmer = require('../../datadog-shimmer')
4
4
  const {
5
5
  channel,
6
6
  addHook,
7
+ AsyncResource,
7
8
  } = require('./helpers/instrument')
8
9
 
10
+ // Listeners added to the BulkLoad row stream (`bulkLoad.getRowStream()`) by the caller would
11
+ // otherwise run in whatever async context emits the stream event, losing the span that was active
12
+ // when the listener was registered. Bind each registered listener to its registration context so
13
+ // `'finish'`/`'error'`/`'data'` handlers run in the caller's span, as they did before the tracer
14
+ // moved to AsyncLocalStorage. tedious' own internal listeners are attached before `getRowStream`
15
+ // returns, so only caller listeners are bound.
16
+ const ROW_STREAM_LISTENER_METHODS = ['addListener', 'on', 'once', 'prependListener', 'prependOnceListener']
17
+
18
+ function bindRowStreamListeners (rowStream) {
19
+ for (const method of ROW_STREAM_LISTENER_METHODS) {
20
+ shimmer.wrap(rowStream, method, register => function (eventName, listener) {
21
+ if (typeof listener !== 'function') {
22
+ return register.apply(this, arguments)
23
+ }
24
+ return register.call(this, eventName, AsyncResource.bind(listener))
25
+ })
26
+ }
27
+ return rowStream
28
+ }
29
+
9
30
  addHook({ name: 'tedious', versions: ['>=1.0.0'] }, tedious => {
10
31
  const startCh = channel('apm:tedious:request:start')
11
32
  const finishCh = channel('apm:tedious:request:finish')
12
33
  const errorCh = channel('apm:tedious:request:error')
34
+
35
+ if (typeof tedious.BulkLoad?.prototype?.getRowStream === 'function') {
36
+ shimmer.wrap(tedious.BulkLoad.prototype, 'getRowStream', getRowStream => function () {
37
+ return bindRowStreamListeners(getRowStream.apply(this, arguments))
38
+ })
39
+ }
40
+
13
41
  shimmer.wrap(tedious.Connection.prototype, 'makeRequest', makeRequest => function (request) {
14
42
  if (!startCh.hasSubscribers) {
15
43
  return makeRequest.apply(this, arguments)
@@ -138,11 +138,15 @@ function getProvidedContext () {
138
138
  _ddTestManagementTests: testManagementTests,
139
139
  _ddIsFlakyTestRetriesEnabled: isFlakyTestRetriesEnabled,
140
140
  _ddFlakyTestRetriesCount: flakyTestRetriesCount,
141
+ _ddFlakyTestRetriesIncludesUnnamedProject: flakyTestRetriesIncludesUnnamedProject,
142
+ _ddFlakyTestRetriesProjectNames: flakyTestRetriesProjectNames,
141
143
  _ddIsImpactedTestsEnabled: isImpactedTestsEnabled,
142
144
  _ddModifiedFiles: modifiedFiles,
143
145
  _ddTestSessionId: testSessionId,
144
146
  _ddTestModuleId: testModuleId,
145
147
  _ddTestCommand: testCommand,
148
+ _ddRepositoryRoot: repositoryRoot,
149
+ _ddCodeOwnersEntries: codeOwnersEntries,
146
150
  } = globalThis.__vitest_worker__.providedContext
147
151
 
148
152
  return {
@@ -157,11 +161,15 @@ function getProvidedContext () {
157
161
  testManagementTests,
158
162
  isFlakyTestRetriesEnabled,
159
163
  flakyTestRetriesCount: flakyTestRetriesCount ?? 0,
164
+ flakyTestRetriesIncludesUnnamedProject,
165
+ flakyTestRetriesProjectNames,
160
166
  isImpactedTestsEnabled,
161
167
  modifiedFiles,
162
168
  testSessionId,
163
169
  testModuleId,
164
170
  testCommand,
171
+ repositoryRoot,
172
+ codeOwnersEntries,
165
173
  }
166
174
  } catch {
167
175
  log.error('Vitest workers could not parse provided context, so some features will not work.')
@@ -177,11 +185,15 @@ function getProvidedContext () {
177
185
  testManagementTests: {},
178
186
  isFlakyTestRetriesEnabled: false,
179
187
  flakyTestRetriesCount: 0,
188
+ flakyTestRetriesIncludesUnnamedProject: false,
189
+ flakyTestRetriesProjectNames: undefined,
180
190
  isImpactedTestsEnabled: false,
181
191
  modifiedFiles: {},
182
192
  testSessionId: undefined,
183
193
  testModuleId: undefined,
184
194
  testCommand: undefined,
195
+ repositoryRoot: undefined,
196
+ codeOwnersEntries: undefined,
185
197
  }
186
198
  }
187
199
  }
@@ -472,7 +484,7 @@ async function runMainProcessSetup (ctx, frameworkVersion, testSpecifications) {
472
484
  }
473
485
 
474
486
  if (testSessionConfigurationCh.hasSubscribers) {
475
- const { testSessionId, testModuleId, testCommand } = await getChannelPromise(
487
+ const { testSessionId, testModuleId, testCommand, repositoryRoot, codeOwnersEntries } = await getChannelPromise(
476
488
  testSessionConfigurationCh,
477
489
  frameworkVersion
478
490
  )
@@ -480,6 +492,8 @@ async function runMainProcessSetup (ctx, frameworkVersion, testSpecifications) {
480
492
  _ddTestSessionId: testSessionId,
481
493
  _ddTestModuleId: testModuleId,
482
494
  _ddTestCommand: testCommand,
495
+ _ddRepositoryRoot: repositoryRoot,
496
+ _ddCodeOwnersEntries: codeOwnersEntries,
483
497
  }, 'Could not send test session configuration to workers.')
484
498
  }
485
499
 
@@ -493,11 +507,13 @@ async function runMainProcessSetup (ctx, frameworkVersion, testSpecifications) {
493
507
  getTestManagementTests: () => getChannelPromise(testManagementTestsCh),
494
508
  })
495
509
 
496
- if (isFlakyTestRetriesEnabled && !ctx.config.retry && flakyTestRetriesCount > 0) {
497
- ctx.config.retry = flakyTestRetriesCount
510
+ const flakyTestRetriesConfiguration = configureFlakyTestRetries(ctx, testSpecifications)
511
+ if (flakyTestRetriesConfiguration) {
498
512
  setProvidedContext(ctx, {
499
513
  _ddIsFlakyTestRetriesEnabled: isFlakyTestRetriesEnabled,
500
514
  _ddFlakyTestRetriesCount: flakyTestRetriesCount,
515
+ _ddFlakyTestRetriesIncludesUnnamedProject: flakyTestRetriesConfiguration.includesUnnamedProject,
516
+ _ddFlakyTestRetriesProjectNames: flakyTestRetriesConfiguration.projectNames,
501
517
  }, 'Could not send library configuration to workers.')
502
518
  }
503
519
 
@@ -583,6 +599,242 @@ function ensureMainProcessSetup (ctx, frameworkVersion, testSpecifications) {
583
599
  return setupPromise
584
600
  }
585
601
 
602
+ /**
603
+ * Configure Vitest retries for the root project and resolved workspace projects.
604
+ *
605
+ * @param {object} ctx
606
+ * @param {unknown[]|undefined} testSpecifications
607
+ * @returns {{ projectNames: string[], includesUnnamedProject: boolean }|undefined}
608
+ */
609
+ function configureFlakyTestRetries (ctx, testSpecifications) {
610
+ if (!isFlakyTestRetriesEnabled || flakyTestRetriesCount <= 0) return
611
+
612
+ let configured = false
613
+ let includesUnnamedProject = false
614
+ const projectNames = []
615
+ for (const { config, projectName } of getVitestProjectConfigs(ctx, testSpecifications)) {
616
+ if (!config.retry) {
617
+ config.retry = flakyTestRetriesCount
618
+ configured = true
619
+ if (projectName) {
620
+ projectNames.push(projectName)
621
+ } else {
622
+ includesUnnamedProject = true
623
+ }
624
+ }
625
+ }
626
+
627
+ if (!configured) return
628
+
629
+ return {
630
+ includesUnnamedProject,
631
+ projectNames,
632
+ }
633
+ }
634
+
635
+ /**
636
+ * Return unique Vitest configs that can be used to run tests.
637
+ *
638
+ * @param {object} ctx
639
+ * @param {unknown[]|undefined} testSpecifications
640
+ * @returns {{ config: object, projectName?: string }[]}
641
+ */
642
+ function getVitestProjectConfigs (ctx, testSpecifications) {
643
+ const entries = []
644
+
645
+ addTestSpecificationConfigs(entries, testSpecifications)
646
+ if (entries.length > 0) {
647
+ return entries
648
+ }
649
+
650
+ const selectedProjectNames = getSelectedProjectNames()
651
+ addSelectedInlineProjectConfigs(entries, safeConfig(ctx), selectedProjectNames)
652
+ addSelectedRuntimeProjectConfigs(entries, ctx?.projects, selectedProjectNames)
653
+ if (entries.length > 0) {
654
+ return entries
655
+ }
656
+
657
+ if (Array.isArray(ctx?.projects)) {
658
+ for (const project of ctx.projects) {
659
+ addConfig(entries, safeConfig(project), getProjectName(project))
660
+ }
661
+ if (entries.length > 0) {
662
+ return entries
663
+ }
664
+ }
665
+
666
+ addConfig(entries, safeConfig(ctx))
667
+ addConfig(entries, safeConfig(safeWorkspaceProject(ctx)))
668
+
669
+ return entries
670
+ }
671
+
672
+ /**
673
+ * Add configs from runnable test specifications once.
674
+ *
675
+ * @param {{ config: object, projectName?: string }[]} entries
676
+ * @param {unknown[]|undefined} testSpecifications
677
+ */
678
+ function addTestSpecificationConfigs (entries, testSpecifications) {
679
+ if (!Array.isArray(testSpecifications)) return
680
+
681
+ for (const testSpecification of testSpecifications) {
682
+ const project = getTestSpecificationProject(testSpecification)
683
+ addConfig(entries, safeConfig(project), getProjectName(project))
684
+ }
685
+ }
686
+
687
+ /**
688
+ * Add selected inline project configs from the root Vitest config once.
689
+ *
690
+ * @param {{ config: object, projectName?: string }[]} entries
691
+ * @param {object|undefined} rootConfig
692
+ * @param {string[]} selectedProjectNames
693
+ */
694
+ function addSelectedInlineProjectConfigs (entries, rootConfig, selectedProjectNames) {
695
+ if (selectedProjectNames.length === 0 || !Array.isArray(rootConfig?.projects)) return
696
+
697
+ for (const project of rootConfig.projects) {
698
+ const config = getInlineProjectConfig(project)
699
+ const projectName = getProjectName(project)
700
+ if (selectedProjectNames.includes(projectName)) {
701
+ addConfig(entries, config, projectName)
702
+ }
703
+ }
704
+ }
705
+
706
+ /**
707
+ * Add selected resolved project configs once.
708
+ *
709
+ * @param {{ config: object, projectName?: string }[]} entries
710
+ * @param {unknown[]|undefined} projects
711
+ * @param {string[]} selectedProjectNames
712
+ */
713
+ function addSelectedRuntimeProjectConfigs (entries, projects, selectedProjectNames) {
714
+ if (selectedProjectNames.length === 0 || !Array.isArray(projects)) return
715
+
716
+ for (const project of projects) {
717
+ const projectName = getProjectName(project)
718
+ if (selectedProjectNames.includes(projectName)) {
719
+ addConfig(entries, safeConfig(project), projectName)
720
+ }
721
+ }
722
+ }
723
+
724
+ /**
725
+ * Return selected project names from the Vitest CLI arguments.
726
+ *
727
+ * @returns {string[]}
728
+ */
729
+ function getSelectedProjectNames () {
730
+ const names = []
731
+ for (let index = 0; index < process.argv.length; index++) {
732
+ const argument = process.argv[index]
733
+ if (argument === '--project' && process.argv[index + 1]) {
734
+ names.push(process.argv[index + 1])
735
+ index++
736
+ } else if (argument.startsWith('--project=')) {
737
+ names.push(argument.slice('--project='.length))
738
+ }
739
+ }
740
+ return names
741
+ }
742
+
743
+ /**
744
+ * Return the test config from an inline Vitest project entry.
745
+ *
746
+ * @param {unknown} project
747
+ * @returns {object|undefined}
748
+ */
749
+ function getInlineProjectConfig (project) {
750
+ return project?.test || project
751
+ }
752
+
753
+ /**
754
+ * Return a Vitest project name from runtime or inline project objects.
755
+ *
756
+ * @param {unknown} project
757
+ * @returns {string|undefined}
758
+ */
759
+ function getProjectName (project) {
760
+ return normalizeProjectName(project?.name || project?.config?.name || project?.test?.name)
761
+ }
762
+
763
+ /**
764
+ * Return a normalized Vitest project name.
765
+ *
766
+ * @param {unknown} name
767
+ * @returns {string|undefined}
768
+ */
769
+ function normalizeProjectName (name) {
770
+ if (typeof name === 'string') return name
771
+
772
+ const label = name?.label
773
+ return typeof label === 'string' ? label : undefined
774
+ }
775
+
776
+ /**
777
+ * Add a config object once.
778
+ *
779
+ * @param {{ config: object, projectName?: string }[]} entries
780
+ * @param {object|undefined} config
781
+ * @param {string|undefined} projectName
782
+ */
783
+ function addConfig (entries, config, projectName) {
784
+ if (config && !entries.some(entry => entry.config === config || (projectName && entry.projectName === projectName))) {
785
+ entries.push({ config, projectName })
786
+ }
787
+ }
788
+
789
+ /**
790
+ * Read a Vitest config object without assuming the project is initialized.
791
+ *
792
+ * @param {object|undefined} project
793
+ * @returns {object|undefined}
794
+ */
795
+ function safeConfig (project) {
796
+ let config
797
+ try {
798
+ config = project?.config
799
+ } catch {}
800
+ return config
801
+ }
802
+
803
+ /**
804
+ * Read the workspace project without assuming the root server is initialized.
805
+ *
806
+ * @param {object} ctx
807
+ * @returns {object|undefined}
808
+ */
809
+ function safeWorkspaceProject (ctx) {
810
+ let project
811
+ try {
812
+ project = getWorkspaceProject(ctx)
813
+ } catch {}
814
+ return project
815
+ }
816
+
817
+ /**
818
+ * Return whether Datadog configured ATR retries for a task.
819
+ *
820
+ * @param {object} providedContext
821
+ * @param {object} task
822
+ * @returns {boolean}
823
+ */
824
+ function isFlakyTestRetriesEnabledForTask (providedContext, task) {
825
+ if (!providedContext.isFlakyTestRetriesEnabled) return false
826
+
827
+ const { flakyTestRetriesProjectNames } = providedContext
828
+ if (!Array.isArray(flakyTestRetriesProjectNames)) return true
829
+
830
+ const projectName = task.file?.projectName
831
+ if (!projectName) {
832
+ return providedContext.flakyTestRetriesIncludesUnnamedProject === true
833
+ }
834
+
835
+ return flakyTestRetriesProjectNames.includes(projectName)
836
+ }
837
+
586
838
  function getSortWrapper (sort, frameworkVersion) {
587
839
  return async function () {
588
840
  await ensureMainProcessSetup(this.ctx, frameworkVersion, arguments[0])
@@ -653,15 +905,77 @@ function getCliOrStartVitestWrapper (frameworkVersion) {
653
905
  }
654
906
  }
655
907
 
908
+ function isForkPool (pool) {
909
+ return pool === 'forks' || pool === 'vmForks'
910
+ }
911
+
912
+ function isThreadPool (pool) {
913
+ return pool === 'threads' || pool === 'vmThreads'
914
+ }
915
+
916
+ /**
917
+ * Return the project object attached to a Vitest test specification.
918
+ *
919
+ * @param {unknown} testSpecification
920
+ * @returns {object|undefined}
921
+ */
922
+ function getTestSpecificationProject (testSpecification) {
923
+ if (Array.isArray(testSpecification)) {
924
+ return testSpecification[0]
925
+ }
926
+ return testSpecification?.project
927
+ }
928
+
929
+ function getTestSpecificationPool (testSpecification) {
930
+ const project = getTestSpecificationProject(testSpecification)
931
+ return project?.config?.pool || project?.serializedConfig?.pool || project?.pool || testSpecification?.pool
932
+ }
933
+
934
+ function hasForkPoolTestSpecification (testSpecifications) {
935
+ if (!Array.isArray(testSpecifications)) {
936
+ return false
937
+ }
938
+
939
+ for (const testSpecification of testSpecifications) {
940
+ if (isForkPool(getTestSpecificationPool(testSpecification))) {
941
+ return true
942
+ }
943
+ }
944
+
945
+ return false
946
+ }
947
+
948
+ function shouldMarkVitestWorkerEnv (pool, testSpecifications) {
949
+ return isForkPool(pool) || hasForkPoolTestSpecification(testSpecifications) ||
950
+ (!testSpecifications && !isThreadPool(pool))
951
+ }
952
+
953
+ function markVitestWorkerEnv (ctx, testSpecifications) {
954
+ const config = ctx?.config
955
+ if (!config || !shouldMarkVitestWorkerEnv(config.pool, testSpecifications)) {
956
+ return
957
+ }
958
+ config.env = config.env || {}
959
+ config.env.DD_VITEST_WORKER = '1'
960
+ }
961
+
656
962
  function wrapVitestRunFiles (Vitest, frameworkVersion) {
657
963
  if (!Vitest?.prototype?.runFiles) {
658
964
  return
659
965
  }
660
966
 
661
967
  shimmer.wrap(Vitest.prototype, 'runFiles', runFiles => async function (testSpecifications) {
968
+ markVitestWorkerEnv(this, testSpecifications)
662
969
  await ensureMainProcessSetup(this, frameworkVersion, testSpecifications)
663
970
  return runFiles.apply(this, arguments)
664
971
  })
972
+
973
+ if (Vitest.prototype.collectTests) {
974
+ shimmer.wrap(Vitest.prototype, 'collectTests', collectTests => function () {
975
+ markVitestWorkerEnv(this)
976
+ return collectTests.apply(this, arguments)
977
+ })
978
+ }
665
979
  }
666
980
 
667
981
  function getCreateCliWrapper (vitestPackage, frameworkVersion) {
@@ -938,15 +1252,15 @@ function wrapVitestTestRunner (VitestTestRunner) {
938
1252
  let isNew = false
939
1253
  let isQuarantined = false
940
1254
 
1255
+ const providedContext = getProvidedContext()
941
1256
  const {
942
1257
  isKnownTestsEnabled,
943
1258
  isEarlyFlakeDetectionEnabled,
944
1259
  isDiEnabled,
945
1260
  isTestManagementTestsEnabled,
946
1261
  testManagementTests,
947
- isFlakyTestRetriesEnabled,
948
1262
  slowTestRetries,
949
- } = getProvidedContext()
1263
+ } = providedContext
950
1264
 
951
1265
  if (isKnownTestsEnabled) {
952
1266
  isNew = newTasks.has(task)
@@ -1076,7 +1390,7 @@ function wrapVitestTestRunner (VitestTestRunner) {
1076
1390
  }
1077
1391
 
1078
1392
  const isRetryReasonAtr = numAttempt > 0 &&
1079
- isFlakyTestRetriesEnabled &&
1393
+ isFlakyTestRetriesEnabledForTask(providedContext, task) &&
1080
1394
  !isRetryReasonAttemptToFix &&
1081
1395
  !isRetryReasonEfd
1082
1396
 
@@ -1393,6 +1707,8 @@ addHook({
1393
1707
  testSessionId: providedContext.testSessionId,
1394
1708
  testModuleId: providedContext.testModuleId,
1395
1709
  testCommand: providedContext.testCommand,
1710
+ repositoryRoot: providedContext.repositoryRoot,
1711
+ codeOwnersEntries: providedContext.codeOwnersEntries,
1396
1712
  }
1397
1713
  testSuiteStartCh.runStores(testSuiteCtx, () => {})
1398
1714
  const startTestsResponse = await startTests.apply(this, arguments)
@@ -1465,7 +1781,7 @@ addHook({
1465
1781
  }
1466
1782
 
1467
1783
  // ATR: set hasFailedAllRetries when all auto test retries were exhausted and every attempt failed
1468
- const isAtrRetry = providedContext.isFlakyTestRetriesEnabled && !attemptToFixTasks.has(task) &&
1784
+ const isAtrRetry = isFlakyTestRetriesEnabledForTask(providedContext, task) && !attemptToFixTasks.has(task) &&
1469
1785
  !newTasks.has(task) && !modifiedTasks.has(task)
1470
1786
  if (isAtrRetry) {
1471
1787
  const maxRetries = providedContext.flakyTestRetriesCount ?? 0
@@ -43,9 +43,9 @@ class SchemaExtractor {
43
43
  let type
44
44
  let format
45
45
  let enumValues
46
- let description
47
46
  let ref
48
47
 
48
+ const description = field.doc
49
49
  const fieldType = field.type?.types ?? field.type?.typeName ?? field.type
50
50
 
51
51
  if (Array.isArray(fieldType)) {
@@ -129,7 +129,7 @@ class SchemaExtractor {
129
129
  }
130
130
 
131
131
  iterateOverSchema (builder) {
132
- this.constructor.extractSchema(this.schema, builder, 0)
132
+ SchemaExtractor.extractSchema(this.schema, builder, 0)
133
133
  }
134
134
 
135
135
  static attachSchemaOnSpan (args, span, operation, tracer) {
@@ -0,0 +1,31 @@
1
+ 'use strict'
2
+
3
+ const TracingPlugin = require('../../dd-trace/src/plugins/tracing')
4
+
5
+ // On retries the SDK suspends execution without firing error/asyncEnd; finish the span here.
6
+ class AwsDurableExecutionSdkJsCheckpointPlugin extends TracingPlugin {
7
+ static id = 'aws-durable-execution-sdk-js'
8
+ static prefix = 'tracing:orchestrion:@aws/durable-execution-sdk-js:CheckpointManager_checkpoint'
9
+
10
+ start (ctx) {
11
+ const data = ctx.arguments?.[1]
12
+ if (data?.Action !== 'RETRY' || !data.Error) return
13
+
14
+ const span = this.activeSpan
15
+ if (!span || span.context().getTag('error')) return
16
+
17
+ const { ErrorMessage, ErrorType, StackTrace } = data.Error
18
+ span.setTag('error', 1)
19
+ if (ErrorMessage) span.setTag('error.message', ErrorMessage)
20
+ if (ErrorType) span.setTag('error.type', ErrorType)
21
+ if (Array.isArray(StackTrace)) span.setTag('error.stack', StackTrace.join('\n'))
22
+
23
+ ctx.retryStepSpan = span
24
+ }
25
+
26
+ asyncEnd (ctx) {
27
+ ctx.retryStepSpan?.finish()
28
+ }
29
+ }
30
+
31
+ module.exports = AwsDurableExecutionSdkJsCheckpointPlugin
@@ -0,0 +1,55 @@
1
+ 'use strict'
2
+
3
+ const ClientPlugin = require('../../dd-trace/src/plugins/client')
4
+ const { addOpMeta, unwrapDurableError } = require('./util')
5
+
6
+ class AwsDurableExecutionSdkJsClientPlugin extends ClientPlugin {
7
+ static id = 'aws-durable-execution-sdk-js'
8
+ static type = 'serverless'
9
+ static prefix = 'tracing:orchestrion:@aws/durable-execution-sdk-js:DurableContextImpl_invoke'
10
+ static settleChannel = 'apm:aws-durable-execution-sdk-js:invoke:settle'
11
+
12
+ constructor (...args) {
13
+ super(...args)
14
+ this.addSub(this.constructor.settleChannel, ctx => this.settle(ctx))
15
+ }
16
+
17
+ // invoke has two overloads: invoke(name, funcId, ...) and invoke(funcId, ...).
18
+ // They're distinguished by whether args[1] is a string (named form) or not.
19
+ bindStart (ctx) {
20
+ const args = ctx.arguments || []
21
+ const isNamed = typeof args[1] === 'string'
22
+ const operationName = isNamed ? args[0] : undefined
23
+ const functionName = isNamed ? args[1] : args[0]
24
+
25
+ const meta = {}
26
+ if (functionName) {
27
+ meta['aws.durable.invoke.function_name'] = functionName
28
+ }
29
+ if (operationName) {
30
+ meta['aws.durable.operation_name'] = operationName
31
+ }
32
+ addOpMeta(meta, ctx.self)
33
+
34
+ this.startSpan(this.operationName(), {
35
+ resource: operationName,
36
+ kind: this.constructor.kind,
37
+ meta,
38
+ }, ctx)
39
+
40
+ return ctx.currentStore
41
+ }
42
+
43
+ settle (ctx) {
44
+ if (ctx.error !== undefined) {
45
+ ctx.currentStore?.span?.setTag('error', unwrapDurableError(ctx))
46
+ }
47
+ this.finish(ctx)
48
+ }
49
+
50
+ error (ctxOrError) {
51
+ this.settle(ctxOrError)
52
+ }
53
+ }
54
+
55
+ module.exports = AwsDurableExecutionSdkJsClientPlugin