dd-trace 6.21.0 → 6.22.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 (204) hide show
  1. package/ci/diagnose.js +9 -14
  2. package/ci/init.js +1 -2
  3. package/ci/test-optimization-validation/ci-discovery.js +9 -4
  4. package/ci/test-optimization-validation/executable.js +1 -5
  5. package/ci/test-optimization-validation/framework-adapters/cypress.js +1 -4
  6. package/ci/test-optimization-validation/framework-adapters/vitest.js +1 -1
  7. package/ci/test-optimization-validation/generated-files.js +6 -4
  8. package/ci/test-optimization-validation/generated-test-contract.js +7 -3
  9. package/ci/test-optimization-validation/manifest-scaffold.js +7 -2
  10. package/ci/test-optimization-validation/manifest-schema.js +6 -7
  11. package/ci/test-optimization-validation/report-writer.js +6 -2
  12. package/ci/test-optimization-validation/runner-command.js +2 -4
  13. package/ci/test-optimization-validation/runner-contract.js +1 -2
  14. package/ci/test-optimization-validation/scenarios/auto-test-retries.js +1 -15
  15. package/ci/vitest-no-worker-init-setup.mjs +10 -7
  16. package/index.d.ts +25 -14
  17. package/initialize.mjs +1 -3
  18. package/openfeature/rules-based.d.ts +11 -0
  19. package/openfeature/rules-based.js +7 -0
  20. package/package.json +12 -10
  21. package/packages/datadog-core/src/storage.js +16 -0
  22. package/packages/datadog-esbuild/index.js +18 -12
  23. package/packages/datadog-instrumentations/src/aerospike.js +3 -3
  24. package/packages/datadog-instrumentations/src/ai.js +3 -3
  25. package/packages/datadog-instrumentations/src/aws-durable-execution-sdk-js.js +0 -6
  26. package/packages/datadog-instrumentations/src/aws-sdk.js +21 -13
  27. package/packages/datadog-instrumentations/src/cassandra-driver.js +25 -20
  28. package/packages/datadog-instrumentations/src/claude-agent-sdk.js +9 -23
  29. package/packages/datadog-instrumentations/src/cucumber.js +48 -33
  30. package/packages/datadog-instrumentations/src/elasticsearch.js +1 -5
  31. package/packages/datadog-instrumentations/src/electron.js +6 -6
  32. package/packages/datadog-instrumentations/src/fastify.js +13 -4
  33. package/packages/datadog-instrumentations/src/fs.js +2 -2
  34. package/packages/datadog-instrumentations/src/google-cloud-pubsub.js +9 -5
  35. package/packages/datadog-instrumentations/src/grpc/client.js +2 -0
  36. package/packages/datadog-instrumentations/src/helpers/bundler-register.js +2 -2
  37. package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +1 -2
  38. package/packages/datadog-instrumentations/src/helpers/instrument.js +1 -1
  39. package/packages/datadog-instrumentations/src/helpers/register.js +36 -1
  40. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +2 -0
  41. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentation-registry.js +87 -6
  42. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql-jit.js +5 -6
  43. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/postgres.js +48 -31
  44. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +7 -7
  45. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms/postgres.js +42 -4
  46. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +7 -10
  47. package/packages/datadog-instrumentations/src/ioredis.js +7 -5
  48. package/packages/datadog-instrumentations/src/iovalkey.js +7 -5
  49. package/packages/datadog-instrumentations/src/jest.js +3 -10
  50. package/packages/datadog-instrumentations/src/knex.js +9 -5
  51. package/packages/datadog-instrumentations/src/mariadb-bundle.js +3 -3
  52. package/packages/datadog-instrumentations/src/mocha/common.js +20 -8
  53. package/packages/datadog-instrumentations/src/mocha/main.js +140 -43
  54. package/packages/datadog-instrumentations/src/mocha/utils.js +3 -2
  55. package/packages/datadog-instrumentations/src/mocha/worker.js +1 -2
  56. package/packages/datadog-instrumentations/src/mongodb-core.js +3 -3
  57. package/packages/datadog-instrumentations/src/mongoose.js +3 -3
  58. package/packages/datadog-instrumentations/src/nova-sonic/audio.js +173 -0
  59. package/packages/datadog-instrumentations/src/nova-sonic/index.js +209 -0
  60. package/packages/datadog-instrumentations/src/nova-sonic/session.js +483 -0
  61. package/packages/datadog-instrumentations/src/openai-realtime/index.js +18 -6
  62. package/packages/datadog-instrumentations/src/openai-realtime/session.js +1 -1
  63. package/packages/datadog-instrumentations/src/openai.js +7 -0
  64. package/packages/datadog-instrumentations/src/pino.js +5 -6
  65. package/packages/datadog-instrumentations/src/redis.js +27 -13
  66. package/packages/datadog-instrumentations/src/router.js +30 -0
  67. package/packages/datadog-instrumentations/src/rum-browser-scripts.js +2 -6
  68. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +7 -5
  69. package/packages/datadog-instrumentations/src/vitest-main.js +9 -15
  70. package/packages/datadog-instrumentations/src/vitest-util.js +2 -2
  71. package/packages/datadog-instrumentations/src/vitest-worker.js +1 -2
  72. package/packages/datadog-instrumentations/src/vitest.js +2 -2
  73. package/packages/datadog-instrumentations/src/webdriverio.js +5 -8
  74. package/packages/datadog-instrumentations/src/ws.js +1 -4
  75. package/packages/datadog-plugin-amqplib/src/client.js +1 -2
  76. package/packages/datadog-plugin-avsc/src/schema_iterator.js +1 -5
  77. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/handler.js +1 -2
  78. package/packages/datadog-plugin-aws-sdk/src/base.js +5 -8
  79. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/index.js +18 -0
  80. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/sonic.js +45 -0
  81. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +9 -3
  82. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +2 -5
  83. package/packages/datadog-plugin-azure-durable-functions/src/index.js +5 -4
  84. package/packages/datadog-plugin-azure-functions/src/index.js +5 -3
  85. package/packages/datadog-plugin-claude-agent-sdk/src/index.js +0 -2
  86. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +1 -5
  87. package/packages/datadog-plugin-cypress/src/index.js +6 -6
  88. package/packages/datadog-plugin-cypress/src/source-map-utils.js +1 -2
  89. package/packages/datadog-plugin-cypress/src/support.js +1 -4
  90. package/packages/datadog-plugin-express/src/code_origin.js +1 -2
  91. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -2
  92. package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +1 -2
  93. package/packages/datadog-plugin-graphql/src/execute.js +5 -5
  94. package/packages/datadog-plugin-grpc/src/util.js +1 -2
  95. package/packages/datadog-plugin-kafkajs/src/producer.js +1 -2
  96. package/packages/datadog-plugin-mocha/src/index.js +7 -10
  97. package/packages/datadog-plugin-mocha/src/webdriverio-video.js +8 -4
  98. package/packages/datadog-plugin-mongodb-core/src/query.js +8 -5
  99. package/packages/datadog-plugin-next/src/index.js +1 -2
  100. package/packages/datadog-plugin-openai/src/tracing.js +5 -2
  101. package/packages/datadog-plugin-openai-agents/src/integration.js +6 -2
  102. package/packages/datadog-plugin-openai-agents/src/processor.js +2 -4
  103. package/packages/datadog-plugin-postgres/src/index.js +24 -0
  104. package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +1 -5
  105. package/packages/datadog-plugin-rhea/src/consumer.js +1 -4
  106. package/packages/datadog-plugin-vitest/src/index.js +5 -2
  107. package/packages/datadog-shimmer/src/shimmer.js +5 -4
  108. package/packages/datadog-webpack/index.js +55 -43
  109. package/packages/datadog-webpack/src/loader.js +56 -8
  110. package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +2 -4
  111. package/packages/dd-trace/src/aiguard/messages/utils.js +1 -2
  112. package/packages/dd-trace/src/aiguard/redaction.js +8 -2
  113. package/packages/dd-trace/src/appsec/api_security/normalized-route.js +3 -2
  114. package/packages/dd-trace/src/appsec/api_security/sampler.js +2 -6
  115. package/packages/dd-trace/src/appsec/downstream_requests.js +1 -9
  116. package/packages/dd-trace/src/appsec/handlers/http-request.js +3 -9
  117. package/packages/dd-trace/src/appsec/handlers/http-response.js +7 -2
  118. package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +4 -3
  119. package/packages/dd-trace/src/appsec/iast/analyzers/set-cookies-header-interceptor.js +1 -4
  120. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations-taint-object.js +7 -1
  121. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +1 -3
  122. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-esm.mjs +1 -3
  123. package/packages/dd-trace/src/appsec/iast/telemetry/iast-metric.js +1 -6
  124. package/packages/dd-trace/src/appsec/lambda.js +1 -3
  125. package/packages/dd-trace/src/appsec/rasp/fs-plugin.js +37 -27
  126. package/packages/dd-trace/src/appsec/rasp/lfi.js +9 -5
  127. package/packages/dd-trace/src/appsec/reporter.js +21 -1
  128. package/packages/dd-trace/src/appsec/stack_trace.js +1 -2
  129. package/packages/dd-trace/src/ci-visibility/dynamic-atr-retries.js +3 -6
  130. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/msgpack-to-json.js +1 -1
  131. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +13 -7
  132. package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +1 -2
  133. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +2 -8
  134. package/packages/dd-trace/src/ci-visibility/resolve-runfile-path.js +54 -0
  135. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +5 -8
  136. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +2 -53
  137. package/packages/dd-trace/src/config/config-types.d.ts +2 -2
  138. package/packages/dd-trace/src/config/defaults.js +5 -5
  139. package/packages/dd-trace/src/config/generated-config-types.d.ts +8 -0
  140. package/packages/dd-trace/src/config/helper.js +34 -3
  141. package/packages/dd-trace/src/config/index.js +10 -9
  142. package/packages/dd-trace/src/config/normalize-service.js +1 -1
  143. package/packages/dd-trace/src/config/parsers.js +2 -2
  144. package/packages/dd-trace/src/config/supported-configurations.json +35 -0
  145. package/packages/dd-trace/src/datastreams/processor.js +3 -2
  146. package/packages/dd-trace/src/datastreams/schemas/schema_builder.js +1 -4
  147. package/packages/dd-trace/src/debugger/devtools_client/send.js +1 -0
  148. package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -0
  149. package/packages/dd-trace/src/debugger/guardrail-metrics.js +2 -2
  150. package/packages/dd-trace/src/dogstatsd.js +1 -1
  151. package/packages/dd-trace/src/encode/tags-processors.js +2 -2
  152. package/packages/dd-trace/src/exporters/common/request.js +1 -5
  153. package/packages/dd-trace/src/id.js +14 -1
  154. package/packages/dd-trace/src/lambda/handler.js +179 -19
  155. package/packages/dd-trace/src/lambda/index.js +15 -1
  156. package/packages/dd-trace/src/lambda/runtime/patch.js +34 -7
  157. package/packages/dd-trace/src/llmobs/audio-utils.js +3 -2
  158. package/packages/dd-trace/src/llmobs/constants/tags.js +12 -0
  159. package/packages/dd-trace/src/llmobs/experiments/experiment.js +1 -1
  160. package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +15 -0
  161. package/packages/dd-trace/src/llmobs/plugins/genai/util.js +1 -2
  162. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chat_model.js +1 -3
  163. package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
  164. package/packages/dd-trace/src/llmobs/plugins/nova-sonic.js +92 -0
  165. package/packages/dd-trace/src/llmobs/plugins/openai/index.js +56 -2
  166. package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +38 -0
  167. package/packages/dd-trace/src/llmobs/prompts/manager.js +5 -7
  168. package/packages/dd-trace/src/llmobs/prompts/prompt.js +1 -1
  169. package/packages/dd-trace/src/llmobs/span_processor.js +1 -2
  170. package/packages/dd-trace/src/llmobs/tagger.js +2 -2
  171. package/packages/dd-trace/src/llmobs/util.js +1 -2
  172. package/packages/dd-trace/src/llmobs/writers/base.js +4 -5
  173. package/packages/dd-trace/src/log/log.js +1 -5
  174. package/packages/dd-trace/src/openfeature/evaluation-telemetry-opt-out-hook.js +51 -0
  175. package/packages/dd-trace/src/openfeature/flagging_provider.js +48 -21
  176. package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +1 -1
  177. package/packages/dd-trace/src/opentelemetry/metrics/otlp_transformer.js +1 -1
  178. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +1 -2
  179. package/packages/dd-trace/src/opentelemetry/otlp/common.proto +1 -1
  180. package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +6 -23
  181. package/packages/dd-trace/src/opentracing/propagation/text_map.js +53 -13
  182. package/packages/dd-trace/src/otel-sampling.js +4 -5
  183. package/packages/dd-trace/src/otel-thread-ctx.js +79 -13
  184. package/packages/dd-trace/src/plugins/database.js +7 -16
  185. package/packages/dd-trace/src/plugins/util/git.js +9 -25
  186. package/packages/dd-trace/src/plugins/util/inferred_proxy.js +1 -5
  187. package/packages/dd-trace/src/plugins/util/test.js +48 -4
  188. package/packages/dd-trace/src/priority_sampler.js +1 -2
  189. package/packages/dd-trace/src/profiling/profiler.js +4 -6
  190. package/packages/dd-trace/src/proxy.js +1 -2
  191. package/packages/dd-trace/src/ritm.js +4 -2
  192. package/packages/dd-trace/src/serverless.js +5 -0
  193. package/packages/dd-trace/src/span_processor.js +13 -17
  194. package/packages/dd-trace/src/telemetry/endpoints.js +1 -3
  195. package/packages/dd-trace/src/util.js +29 -0
  196. package/packages/dd-trace/src/web-tags-cache.js +6 -2
  197. package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
  198. package/vendor/dist/@datadog/openfeature-node-server/rules-based/LICENSE +201 -0
  199. package/vendor/dist/@datadog/openfeature-node-server/rules-based/index.d.ts +925 -0
  200. package/vendor/dist/@datadog/openfeature-node-server/rules-based/index.js +1 -0
  201. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  202. package/vendor/dist/protobufjs/index.js +1 -1
  203. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  204. package/vendor/dist/shell-quote/index.js +1 -1
@@ -42,9 +42,9 @@ function wrapExecuteWithCallback (executeWithCallback) {
42
42
  function wrapProcess (process) {
43
43
  return function (...args) {
44
44
  const cb = args[0]
45
- if (!ch.start.hasSubscribers || typeof cb !== 'function') return process.apply(this, args)
46
-
47
- if (this[kTracingCallbackCommand]) return process.apply(this, args)
45
+ if (!ch.start.hasSubscribers || typeof cb !== 'function' || this[kTracingCallbackCommand]) {
46
+ return process.apply(this, args)
47
+ }
48
48
 
49
49
  const ctx = getContext(this)
50
50
 
@@ -228,11 +228,11 @@ for (const hook of getHooks('ai').values()) {
228
228
 
229
229
  // The SDK builds a model from a string id, in which case only the resolved instance is
230
230
  // worth wrapping; when the caller passed an instance, that is the one the SDK calls.
231
- if (typeof model !== 'string' && model !== ctx.result) {
231
+ if (typeof model === 'string' || model === ctx.result) {
232
+ wrapModel(ctx.result)
233
+ } else {
232
234
  wrapModel(model)
233
235
  wrappedModels.add(ctx.result)
234
- } else {
235
- wrapModel(ctx.result)
236
236
  }
237
237
  },
238
238
  })
@@ -2,7 +2,6 @@
2
2
 
3
3
  const { channel, tracingChannel } = require('dc-polyfill')
4
4
  const shimmer = require('../../datadog-shimmer')
5
- const { addHook, getHooks } = require('./helpers/instrument')
6
5
  const LAZY_DURABLE_PROMISE_METHODS = require('./aws-durable-execution-sdk-js-context-methods')
7
6
 
8
7
  for (const method of LAZY_DURABLE_PROMISE_METHODS) {
@@ -102,8 +101,3 @@ function observeDurablePromise (dp, onSettle) {
102
101
  instrumentDurablePromiseProto(Object.getPrototypeOf(dp))
103
102
  dp[ON_SETTLE] = onSettle
104
103
  }
105
-
106
- for (const hook of getHooks('@aws/durable-execution-sdk-js').values()) {
107
- hook.file = null
108
- addHook(hook, exports => exports)
109
- }
@@ -2,12 +2,14 @@
2
2
 
3
3
  const shimmer = require('../../datadog-shimmer')
4
4
  const { channel, addHook } = require('./helpers/instrument')
5
+ const sendSonic = require('./nova-sonic')
5
6
 
6
7
  const patchedClientConfigProtocols = new WeakSet()
7
8
  const patchedCommandPrototypes = new WeakSet()
9
+ const patchedSmithyClients = new WeakSet()
8
10
 
9
11
  // Resource identifiers that already match the channel-suffix slug. Anything
10
- // else falls back to `'default'`. Hoisted out of the per-call hot path so we
12
+ // else falls back to `'aws'`. Hoisted out of the per-call hot path so we
11
13
  // don't allocate a fresh Array literal + run `.includes` on every AWS send.
12
14
  const KNOWN_CHANNEL_SUFFIXES = new Set([
13
15
  'cloudwatchlogs',
@@ -161,6 +163,9 @@ function wrapSmithySend (send) {
161
163
  const channels = getChannelBag(channelSuffix)
162
164
  const clientName = getClientName(this.constructor)
163
165
  const operation = getOperationName(command.constructor)
166
+ if (channelSuffix === 'bedrockruntime' && operation === 'invokeModelWithBidirectionalStream') {
167
+ return sendSonic(send, this, command, args)
168
+ }
164
169
  const request = {
165
170
  operation,
166
171
  params: command.input,
@@ -320,18 +325,24 @@ function getChannelSuffix (name) {
320
325
  // some resource identifiers have spaces between ex: bedrock runtime
321
326
  name = String(name).replaceAll(' ', '')
322
327
  if (KNOWN_CHANNEL_SUFFIXES.has(name)) return name
323
- return CHANNEL_SUFFIX_ALIASES.get(name) ?? 'default'
328
+ return CHANNEL_SUFFIX_ALIASES.get(name) ?? 'aws'
324
329
  }
325
330
 
326
- addHook({ name: '@smithy/smithy-client', versions: ['>=1.0.3'] }, smithy => {
327
- shimmer.wrap(smithy.Client.prototype, 'send', wrapSmithySend)
331
+ /** @param {{ Client: Function }} smithy */
332
+ function patchSmithyClient (smithy) {
333
+ const prototype = smithy.Client.prototype
334
+ // Recent smithy-client releases re-export core/client. Loading both entry points must not
335
+ // install two observers on the same send (and consume/charge every duplex event twice).
336
+ if (!patchedSmithyClients.has(prototype)) {
337
+ shimmer.wrap(prototype, 'send', wrapSmithySend)
338
+ patchedSmithyClients.add(prototype)
339
+ }
328
340
  return smithy
329
- })
341
+ }
330
342
 
331
- addHook({ name: '@aws-sdk/smithy-client', versions: ['>=3'] }, smithy => {
332
- shimmer.wrap(smithy.Client.prototype, 'send', wrapSmithySend)
333
- return smithy
334
- })
343
+ addHook({ name: '@smithy/smithy-client', versions: ['>=1.0.3'] }, patchSmithyClient)
344
+
345
+ addHook({ name: '@aws-sdk/smithy-client', versions: ['>=3'] }, patchSmithyClient)
335
346
 
336
347
  // `@aws-sdk/client-*` >= 3.1046.0 dropped `@smithy/smithy-client` and now
337
348
  // extends from `@smithy/core/client` directly. The `Client.send` contract is
@@ -341,10 +352,7 @@ addHook({
341
352
  name: '@smithy/core',
342
353
  file: 'dist-cjs/submodules/client/index.js',
343
354
  versions: ['>=3.24.0'],
344
- }, smithyCoreClient => {
345
- shimmer.wrap(smithyCoreClient.Client.prototype, 'send', wrapSmithySend)
346
- return smithyCoreClient
347
- })
355
+ }, patchSmithyClient)
348
356
 
349
357
  addHook({ name: 'aws-sdk', versions: ['>=2.3.0'] }, AWS => {
350
358
  shimmer.wrap(AWS.config, 'setPromisesDependency', setPromisesDependency => {
@@ -11,6 +11,7 @@ const finishCh = channel('apm:cassandra-driver:query:finish')
11
11
  const errorCh = channel('apm:cassandra-driver:query:error')
12
12
  const connectCh = channel('apm:cassandra-driver:query:connect')
13
13
 
14
+ /** @type {Record<string, unknown> & { error?: Error }} */
14
15
  let startCtx = {}
15
16
 
16
17
  addHook({ name: 'cassandra-driver', versions: ['>=3.0.0'] }, cassandra => {
@@ -24,23 +25,23 @@ addHook({ name: 'cassandra-driver', versions: ['>=3.0.0'] }, cassandra => {
24
25
  startCtx = { keyspace: this.keyspace, query: queries, contactPoints: this.options && this.options.contactPoints }
25
26
  return startCh.runStores(startCtx, () => {
26
27
  if (typeof cb === 'function') {
27
- arguments[lastIndex] = wrapCallback(finishCh, errorCh, startCtx, cb)
28
+ arguments[lastIndex] = wrapCallback(startCtx, cb)
28
29
  }
29
30
 
30
31
  try {
31
32
  const res = batch.apply(this, arguments)
32
33
  if (typeof res === 'function') {
33
- return wrapCallback(finishCh, errorCh, startCtx, res)
34
+ return wrapCallback(startCtx, res)
34
35
  }
35
36
  if (!res) {
36
37
  return res
37
38
  }
38
39
  return res.then(
39
- () => finish(finishCh, errorCh, startCtx),
40
- err => finish(finishCh, errorCh, startCtx, err)
40
+ () => finish(startCtx),
41
+ err => finish(startCtx, err)
41
42
  )
42
43
  } catch (e) {
43
- finish(finishCh, errorCh, startCtx, e)
44
+ finish(startCtx, e)
44
45
  throw e
45
46
  }
46
47
  })
@@ -58,8 +59,8 @@ addHook({ name: 'cassandra-driver', versions: ['>=4.4'], patchDefault: false },
58
59
  const promise = _execute.apply(this, arguments)
59
60
 
60
61
  promise.then(
61
- () => finish(finishCh, errorCh, startCtx),
62
- err => finish(finishCh, errorCh, startCtx, err)
62
+ () => finish(startCtx),
63
+ err => finish(startCtx, err)
63
64
  )
64
65
  return promise
65
66
  })
@@ -74,10 +75,7 @@ const isValid = (args) => {
74
75
  addHook({ name: 'cassandra-driver', versions: ['3 - 4.3'], patchDefault: false }, (cassandra) => {
75
76
  shimmer.wrap(cassandra.Client.prototype, '_innerExecute', _innerExecute =>
76
77
  function (query, params, execOptions, callback) {
77
- if (!startCh.hasSubscribers) {
78
- return _innerExecute.apply(this, arguments)
79
- }
80
- if (!isValid(arguments)) {
78
+ if (!startCh.hasSubscribers || !isValid(arguments)) {
81
79
  return _innerExecute.apply(this, arguments)
82
80
  }
83
81
 
@@ -87,13 +85,13 @@ addHook({ name: 'cassandra-driver', versions: ['3 - 4.3'], patchDefault: false }
87
85
  const cb = arguments[lastIndex]
88
86
 
89
87
  if (typeof cb === 'function') {
90
- arguments[lastIndex] = wrapCallback(finishCh, errorCh, startCtx, cb)
88
+ arguments[lastIndex] = wrapCallback(startCtx, cb)
91
89
  }
92
90
 
93
91
  try {
94
92
  return _innerExecute.apply(this, arguments)
95
93
  } catch (e) {
96
- finish(finishCh, errorCh, startCtx, e)
94
+ finish(startCtx, e)
97
95
  throw e
98
96
  }
99
97
  })
@@ -156,7 +154,11 @@ addHook({ name: 'cassandra-driver', versions: ['3 - 3.2'], file: 'lib/request-ha
156
154
  return RequestHandler
157
155
  })
158
156
 
159
- function finish (finishCh, errorCh, ctx, error) {
157
+ /**
158
+ * @param {Record<string, unknown> & { error?: Error }} ctx
159
+ * @param {Error} [error]
160
+ */
161
+ function finish (ctx, error) {
160
162
  if (error) {
161
163
  ctx.error = error
162
164
  errorCh.publish(ctx)
@@ -164,19 +166,22 @@ function finish (finishCh, errorCh, ctx, error) {
164
166
  finishCh.runStores(ctx, () => {})
165
167
  }
166
168
 
167
- function wrapCallback (finishCh, errorCh, ctx, callback) {
169
+ /**
170
+ * @param {Record<string, unknown> & { error?: Error }} ctx
171
+ * @param {Parameters<typeof finishCh.runStores>[1]} callback
172
+ */
173
+ function wrapCallback (ctx, callback) {
168
174
  return shimmer.wrapCallback(callback, callback => function (err) {
169
175
  if (err) {
170
176
  ctx.error = err
171
177
  errorCh.publish(ctx)
172
178
  }
173
- return finishCh.runStores(ctx, callback, this, ...arguments)
179
+ return finishCh.runStores(
180
+ ctx, /** @type {Parameters<typeof finishCh.runStores>[1]} */ (callback), this, ...arguments
181
+ )
174
182
  })
175
183
  }
176
184
 
177
185
  function isRequestValid (exec, args, length) {
178
- if (!exec) return false
179
- if (args.length !== length || typeof args[length - 1] !== 'function') return false
180
-
181
- return true
186
+ return !!exec && args.length === length && typeof args[length - 1] === 'function'
182
187
  }
@@ -2,7 +2,6 @@
2
2
 
3
3
  const { tracingChannel } = require('dc-polyfill')
4
4
  const shimmer = require('../../datadog-shimmer')
5
- const { addHook, getHooks } = require('./helpers/instrument')
6
5
  const queryChannel = tracingChannel('orchestrion:@anthropic-ai/claude-agent-sdk:query')
7
6
 
8
7
  const stepCh = tracingChannel('apm:claude-agent-sdk:step')
@@ -638,28 +637,15 @@ function wrapQueryAsyncIterator (asyncIterator, ctx) {
638
637
  }
639
638
  }
640
639
 
641
- let querySubscribed = false
640
+ queryChannel.subscribe({
641
+ start: onQueryStart,
642
+ end (ctx) {
643
+ if (!ctx.claudeAgentSdkTracing) return
642
644
 
643
- for (const hook of getHooks('@anthropic-ai/claude-agent-sdk').values()) {
644
- hook.file = null
645
+ const { result } = ctx
645
646
 
646
- addHook(hook, exports => {
647
- if (!querySubscribed) {
648
- querySubscribed = true
649
- queryChannel.subscribe({
650
- start: onQueryStart,
651
- end (ctx) {
652
- if (!ctx.claudeAgentSdkTracing) return
647
+ ctx.streamResolved = false
653
648
 
654
- const { result } = ctx
655
-
656
- ctx.streamResolved = false
657
-
658
- shimmer.wrap(result, Symbol.asyncIterator, asyncIterator => wrapQueryAsyncIterator(asyncIterator, ctx))
659
- },
660
- })
661
- }
662
-
663
- return exports
664
- })
665
- }
649
+ shimmer.wrap(result, Symbol.asyncIterator, asyncIterator => wrapQueryAsyncIterator(asyncIterator, ctx))
650
+ },
651
+ })
@@ -102,7 +102,7 @@ const efdRetryCountByPickleId = new Map()
102
102
  const efdSlowAbortedPickleIds = new Set()
103
103
  const finishedParallelSuites = new Set()
104
104
  const numAttemptToCtx = new Map()
105
- const newTestsByTestFullname = new Map()
105
+ const efdStatusesByPickleId = new Map()
106
106
  const attemptToFixTestsByTestFullname = new Map()
107
107
  const modifiedTestsByPickleId = new Map()
108
108
  const runnerToRetryState = new WeakMap()
@@ -200,13 +200,13 @@ function getSuiteStatusFromTestStatuses (testStatuses) {
200
200
  return 'pass'
201
201
  }
202
202
 
203
- function publishWorkerEfdRetryCount (pickle, retryCount) {
203
+ function publishWorkerEfdRetryCount (pickle, retryCount, isModified) {
204
204
  const message = {
205
205
  [DD_EFD_RETRY_COUNT_MESSAGE]: {
206
206
  pickleId: pickle.id,
207
207
  retryCount,
208
+ isModified,
208
209
  testFileAbsolutePath: pickle.uri,
209
- testName: pickle.name,
210
210
  },
211
211
  }
212
212
 
@@ -220,10 +220,12 @@ function publishWorkerEfdRetryCount (pickle, retryCount) {
220
220
  }
221
221
 
222
222
  try {
223
- const { isMainThread, parentPort } = require('node:worker_threads')
223
+ const { isMainThread, parentPort, workerData } = require('node:worker_threads')
224
224
  if (isMainThread || !parentPort) return
225
225
 
226
- parentPort.postMessage(message)
226
+ // Share Cucumber's envelope port so EFD metadata arrives before test results.
227
+ const port = workerData?.port ?? parentPort
228
+ port.postMessage(message)
227
229
  } catch {
228
230
  // ignore IPC errors
229
231
  }
@@ -385,14 +387,16 @@ function handleParallelTestCaseFinished (pickle, worstTestStepResult, usesNumeri
385
387
  const testFileAbsolutePath = pickle.uri
386
388
  const finished = (pickleResultByFile[testFileAbsolutePath] ||= [])
387
389
 
388
- if (shouldRunEarlyFlakeDetection() && isNew) {
389
- const testFullname = `${pickle.uri}:${pickle.name}`
390
- let testStatuses = newTestsByTestFullname.get(testFullname)
390
+ const isAttemptToFix = isTestManagementTestsEnabled &&
391
+ getTestProperties(getTestSuitePath(testFileAbsolutePath, process.cwd()), pickle.name).attemptToFix
392
+
393
+ if (!isAttemptToFix && shouldRunEarlyFlakeDetection() && (isNew || modifiedTestsByPickleId.get(pickle.id))) {
394
+ let testStatuses = efdStatusesByPickleId.get(pickle.id)
391
395
  if (testStatuses) {
392
396
  testStatuses.push(status)
393
397
  } else {
394
398
  testStatuses = [status]
395
- newTestsByTestFullname.set(testFullname, testStatuses)
399
+ efdStatusesByPickleId.set(pickle.id, testStatuses)
396
400
  }
397
401
  let efdRetryCount = efdRetryCountByPickleId.get(pickle.id)
398
402
  if (efdRetryCount === undefined) {
@@ -404,11 +408,8 @@ function handleParallelTestCaseFinished (pickle, worstTestStepResult, usesNumeri
404
408
  efdSlowAbortedPickleIds.add(pickle.id)
405
409
  }
406
410
  }
407
- maybeRecordFinalParallelEfdStatus({ pickleId: pickle.id, testFileAbsolutePath, testFullname })
408
- } else if (
409
- isTestManagementTestsEnabled &&
410
- getTestProperties(getTestSuitePath(testFileAbsolutePath, process.cwd()), pickle.name).attemptToFix
411
- ) {
411
+ maybeRecordFinalParallelEfdStatus({ pickleId: pickle.id, testFileAbsolutePath })
412
+ } else if (isAttemptToFix) {
412
413
  const testFullname = `${pickle.uri}:${pickle.name}`
413
414
  let testStatuses = attemptToFixTestsByTestFullname.get(testFullname)
414
415
  if (testStatuses) {
@@ -495,8 +496,14 @@ function finishParallelSuiteIfDone (testFileAbsolutePath) {
495
496
  const finished = pickleResultByFile[testFileAbsolutePath]
496
497
  const expectedPickles = pickleByFile[testFileAbsolutePath]
497
498
 
498
- if (!finished || !expectedPickles || finished.length !== expectedPickles.length) return
499
- if (finishedParallelSuites.has(testFileAbsolutePath)) return
499
+ if (
500
+ !finished ||
501
+ !expectedPickles ||
502
+ finished.length !== expectedPickles.length ||
503
+ finishedParallelSuites.has(testFileAbsolutePath)
504
+ ) {
505
+ return
506
+ }
500
507
 
501
508
  finishedParallelSuites.add(testFileAbsolutePath)
502
509
  testSuiteFinishCh.publish({
@@ -505,29 +512,30 @@ function finishParallelSuiteIfDone (testFileAbsolutePath) {
505
512
  })
506
513
  }
507
514
 
508
- function maybeRecordFinalParallelEfdStatus ({ pickleId, testFileAbsolutePath, testFullname }) {
515
+ function maybeRecordFinalParallelEfdStatus ({ pickleId, testFileAbsolutePath }) {
509
516
  const efdRetryCount = efdRetryCountByPickleId.get(pickleId)
510
- const testStatuses = newTestsByTestFullname.get(testFullname)
517
+ const testStatuses = efdStatusesByPickleId.get(pickleId)
511
518
  const finished = pickleResultByFile[testFileAbsolutePath]
512
519
 
513
- if (efdRetryCount === undefined || !testStatuses || !finished) return
514
- if (testStatuses.length !== efdRetryCount + 1) return
520
+ if (efdRetryCount === undefined || !testStatuses || !finished || testStatuses.length !== efdRetryCount + 1) return
515
521
 
516
- finished.push(getTestStatusFromRetries(testStatuses))
517
- newTestsByTestFullname.delete(testFullname)
522
+ finished.push(efdRetryCount === 0 ? testStatuses[0] : getTestStatusFromRetries(testStatuses))
523
+ efdStatusesByPickleId.delete(pickleId)
518
524
  finishParallelSuiteIfDone(testFileAbsolutePath)
519
525
  }
520
526
 
521
527
  function handleEfdRetryCountMessage (message) {
522
- const { pickleId, retryCount, testFileAbsolutePath, testName } = message
528
+ const { pickleId, retryCount, isModified, testFileAbsolutePath } = message
523
529
 
524
- if (!pickleId || typeof retryCount !== 'number' || !testFileAbsolutePath || !testName) return
530
+ if (!pickleId || !testFileAbsolutePath) return
531
+
532
+ if (isModified) modifiedTestsByPickleId.set(pickleId, true)
533
+ if (typeof retryCount !== 'number') return
525
534
 
526
535
  efdRetryCountByPickleId.set(pickleId, retryCount)
527
536
  maybeRecordFinalParallelEfdStatus({
528
537
  pickleId,
529
538
  testFileAbsolutePath,
530
- testFullname: `${testFileAbsolutePath}:${testName}`,
531
539
  })
532
540
  }
533
541
 
@@ -912,12 +920,13 @@ function wrapRun (pl, isLatestVersion, version) {
912
920
  isNew = newTestPickleIds.has(this.pickle.id)
913
921
  }
914
922
 
915
- if (isNew || isModified) {
923
+ if (!isAttemptToFix && (isNew || isModified)) {
916
924
  isEfdRetry = numRetries > 0
917
925
  }
918
926
 
919
927
  if (
920
928
  shouldRunEarlyFlakeDetection() &&
929
+ !isAttemptToFix &&
921
930
  status !== 'skip' &&
922
931
  (isNew || isModified) &&
923
932
  !isEfdRetry &&
@@ -1265,7 +1274,8 @@ function getWrappedStart (start, frameworkVersion, isParallel = false, isCoordin
1265
1274
  efdRetryCountByPickleId.clear()
1266
1275
  efdSlowAbortedPickleIds.clear()
1267
1276
  finishedParallelSuites.clear()
1268
- newTestsByTestFullname.clear()
1277
+ efdStatusesByPickleId.clear()
1278
+ modifiedTestsByPickleId.clear()
1269
1279
  sessionStartCh.publish({ command, frameworkVersion })
1270
1280
 
1271
1281
  if (!errorSkippableRequest && skippedSuites.length) {
@@ -1478,10 +1488,15 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
1478
1488
  }
1479
1489
  }
1480
1490
  const originalRetry = this.options.retry
1481
- const isManagedRetry = isAttemptToFix || (shouldRunEarlyFlakeDetection() && (isNew || isModified))
1491
+ const isEfdTest = !isAttemptToFix && shouldRunEarlyFlakeDetection() && (isNew || isModified)
1492
+ const isManagedRetry = isAttemptToFix || isEfdTest
1482
1493
  if (isManagedRetry) {
1483
1494
  this.options.retry = 0
1484
1495
  }
1496
+ if (isWorker && isModified && isEfdTest) {
1497
+ // The coordinator must recognize modified EFD tests before their first result arrives.
1498
+ publishWorkerEfdRetryCount(pickle, undefined, true)
1499
+ }
1485
1500
  // TODO: for >=11 we could use `runTestCaseResult` instead of accumulating results in `lastStatusByPickleId`
1486
1501
  const firstExecutionStart = performance.now()
1487
1502
  let runTestCaseResult = await runTestCaseFunction.apply(this, arguments)
@@ -1493,7 +1508,7 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
1493
1508
  const testStatuses = lastStatusByPickleId.get(pickle.id)
1494
1509
  const lastTestStatus = testStatuses.at(-1)
1495
1510
 
1496
- // New tests should not be marked as attempt to fix, so EFD + Attempt to fix should not be enabled at the same time
1511
+ // Attempt to Fix owns retries for managed tests, including modified tests.
1497
1512
  if (isAttemptToFix && lastTestStatus !== 'skip') {
1498
1513
  for (let retryIndex = 0; retryIndex < testManagementAttemptToFixRetries; retryIndex++) {
1499
1514
  numRetriesByPickleId.set(pickle.id, retryIndex + 1)
@@ -1502,8 +1517,8 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
1502
1517
  }
1503
1518
  }
1504
1519
 
1505
- // If it's a new test and it hasn't been skipped, we run it again
1506
- if (shouldRunEarlyFlakeDetection() && lastTestStatus !== 'skip' && (isNew || isModified)) {
1520
+ // Retry eligible new or modified tests unless their first attempt was skipped.
1521
+ if (isEfdTest && lastTestStatus !== 'skip') {
1507
1522
  let efdRetryCount = efdRetryCountByPickleId.get(pickle.id)
1508
1523
  if (efdRetryCount === undefined) {
1509
1524
  efdRetryCount = getEfdRetryCountForDuration(firstExecutionDurationMs, earlyFlakeDetectionRetryPolicy)
@@ -1525,7 +1540,7 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
1525
1540
  let shouldBePassedByEFD = false
1526
1541
  let shouldBePassedByTestManagement = false
1527
1542
  let shouldBeFailedByAttemptToFix = false
1528
- if ((isNew || isModified) && shouldRunEarlyFlakeDetection()) {
1543
+ if (isEfdTest) {
1529
1544
  /**
1530
1545
  * If Early Flake Detection (EFD) is enabled the logic is as follows:
1531
1546
  * - If all attempts for a test are failing, the test has failed and we will let the test process fail.
@@ -1587,7 +1602,7 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
1587
1602
  return shouldBePassedByTestManagement
1588
1603
  }
1589
1604
 
1590
- if (isNewerCucumberVersion && shouldRunEarlyFlakeDetection() && (isNew || isModified)) {
1605
+ if (isNewerCucumberVersion && isEfdTest) {
1591
1606
  return shouldBePassedByEFD
1592
1607
  }
1593
1608
 
@@ -72,11 +72,7 @@ function createWrapRequest (name) {
72
72
 
73
73
  return function wrapRequest (request) {
74
74
  return function (params, options, cb) {
75
- if (!startCh.hasSubscribers) {
76
- return request.apply(this, arguments)
77
- }
78
-
79
- if (!params) return request.apply(this, arguments)
75
+ if (!startCh.hasSubscribers || !params) return request.apply(this, arguments)
80
76
 
81
77
  const ctx = { params }
82
78
  return startCh.runStores(ctx, () => {
@@ -83,10 +83,10 @@ function createWrapAddListener (ch, mappings) {
83
83
  }
84
84
  }
85
85
 
86
- function createWrapRemoveListener (mappings) {
86
+ function createWrapRemoveListener () {
87
87
  return function wrapRemoveListener (removeListener) {
88
88
  return function (channel, listener) {
89
- const mapping = mappings[channel]
89
+ const mapping = listeners[channel]
90
90
 
91
91
  if (mapping) {
92
92
  const wrapper = mapping.get(listener)
@@ -212,10 +212,10 @@ addHook({ name: 'electron', versions: ['>=37.0.0'] }, electron => {
212
212
  wrap(ipcRenderer, 'sendToHost', createWrapSend(rendererSendCh))
213
213
 
214
214
  wrap(ipcRenderer, 'addListener', createWrapAddListener(rendererReceiveCh, listeners))
215
- wrap(ipcRenderer, 'off', createWrapRemoveListener(listeners))
215
+ wrap(ipcRenderer, 'off', createWrapRemoveListener())
216
216
  wrap(ipcRenderer, 'on', createWrapAddListener(rendererReceiveCh, listeners))
217
217
  wrap(ipcRenderer, 'once', createWrapAddListener(rendererReceiveCh, listeners))
218
- wrap(ipcRenderer, 'removeListener', createWrapRemoveListener(listeners))
218
+ wrap(ipcRenderer, 'removeListener', createWrapRemoveListener())
219
219
  wrap(ipcRenderer, 'removeAllListeners', createWrapRemoveAllListeners(listeners))
220
220
 
221
221
  ipcRenderer.send('datadog:apm:renderer:patched')
@@ -223,12 +223,12 @@ addHook({ name: 'electron', versions: ['>=37.0.0'] }, electron => {
223
223
  wrap(ipcMain, 'addListener', createWrapAddListener(mainReceiveCh, listeners))
224
224
  wrap(ipcMain, 'handle', createWrapAddListener(mainHandleCh, handlers))
225
225
  wrap(ipcMain, 'handleOnce', createWrapAddListener(mainHandleCh, handlers))
226
- wrap(ipcMain, 'off', createWrapRemoveListener(listeners))
226
+ wrap(ipcMain, 'off', createWrapRemoveListener())
227
227
  wrap(ipcMain, 'on', createWrapAddListener(mainReceiveCh, listeners))
228
228
  wrap(ipcMain, 'once', createWrapAddListener(mainReceiveCh, listeners))
229
229
  wrap(ipcMain, 'removeAllListeners', createWrapRemoveAllListeners(listeners))
230
230
  wrap(ipcMain, 'removeHandler', createWrapRemoveAllListeners(handlers))
231
- wrap(ipcMain, 'removeListener', createWrapRemoveListener(listeners))
231
+ wrap(ipcMain, 'removeListener', createWrapRemoveListener())
232
232
 
233
233
  ipcMain.once('datadog:apm:renderer:patched', event => rendererPatchedCh.publish(event))
234
234
 
@@ -1,6 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const shimmer = require('../../datadog-shimmer')
4
+ const { isEmpty } = require('../../dd-trace/src/util')
4
5
  const { addHook, channel, createErrorPublisher } = require('./helpers/instrument')
5
6
 
6
7
  const errorChannel = channel('apm:fastify:middleware:error')
@@ -186,6 +187,17 @@ function onRequest (request, reply, done) {
186
187
  return done()
187
188
  }
188
189
 
190
+ /**
191
+ * Publishing an empty body would set a WAF address for nothing, so only non-empty bodies are published.
192
+ * Strings, arrays and binary bodies are checked by length to avoid enumerating one key per character or element.
193
+ *
194
+ * @param {unknown} body
195
+ */
196
+ function hasBody (body) {
197
+ if (typeof body === 'string') return body.length > 0
198
+ return typeof body === 'object' && body !== null && !isEmpty(body)
199
+ }
200
+
189
201
  function preHandler (request, reply, done) {
190
202
  if (typeof done !== 'function') return
191
203
  if (!reply || typeof reply.send !== 'function') return done()
@@ -194,11 +206,8 @@ function preHandler (request, reply, done) {
194
206
  const res = getRes(reply)
195
207
  const ctx = { req, res }
196
208
 
197
- // eslint-disable-next-line no-restricted-syntax -- arbitrary body keys; publishing {} sets a WAF address
198
- const hasBody = request.body && Object.keys(request.body).length > 0
199
-
200
209
  // For multipart/form-data, the body is not available until after preValidation hook
201
- if (bodyParserReadCh.hasSubscribers && hasBody && !bodyPublished.has(req)) {
210
+ if (bodyParserReadCh.hasSubscribers && !bodyPublished.has(req) && hasBody(request.body)) {
202
211
  ctx.abortController = new AbortController()
203
212
  ctx.body = request.body
204
213
  bodyParserReadCh.publish(ctx)
@@ -232,9 +232,9 @@ function getMethodParamsRelationByPrefix (prefix) {
232
232
  return paramsByMethod
233
233
  }
234
234
 
235
- function createWatchWrapFunction (override = '') {
235
+ function createWatchWrapFunction () {
236
236
  return function wrapFunction (original) {
237
- const name = override || original.name
237
+ const name = original.name
238
238
  const method = name
239
239
  const operation = name
240
240
  return function (...args) {
@@ -126,10 +126,14 @@ function wrapMethod (method) {
126
126
  }
127
127
  }
128
128
 
129
- function massWrap (obj, methods, wrapper) {
129
+ /**
130
+ * @param {Record<string, Function>} obj
131
+ * @param {string[]} methods
132
+ */
133
+ function massWrap (obj, methods) {
130
134
  for (const method of methods) {
131
135
  if (typeof obj[method] === 'function') {
132
- shimmer.wrap(obj, method, wrapper)
136
+ shimmer.wrap(obj, method, wrapMethod)
133
137
  }
134
138
  }
135
139
  }
@@ -274,11 +278,11 @@ addHook({ name: '@google-cloud/pubsub', versions: ['>=1.2'] }, (obj) => {
274
278
  addHook({ name: '@google-cloud/pubsub', versions: ['>=1.2'] }, (obj) => {
275
279
  const { PublisherClient, SchemaServiceClient, SubscriberClient } = obj.v1
276
280
 
277
- massWrap(PublisherClient.prototype, publisherMethods, wrapMethod)
278
- massWrap(SubscriberClient.prototype, subscriberMethods, wrapMethod)
281
+ massWrap(PublisherClient.prototype, publisherMethods)
282
+ massWrap(SubscriberClient.prototype, subscriberMethods)
279
283
 
280
284
  if (SchemaServiceClient) {
281
- massWrap(SchemaServiceClient.prototype, schemaServiceMethods, wrapMethod)
285
+ massWrap(SchemaServiceClient.prototype, schemaServiceMethods)
282
286
  }
283
287
 
284
288
  return obj
@@ -246,6 +246,8 @@ function patch (hasPeer = false) {
246
246
  return function patch (grpc) {
247
247
  const proto = grpc.Client.prototype
248
248
 
249
+ if (instances.has(proto)) return grpc
250
+
249
251
  instances.set(proto, grpc)
250
252
 
251
253
  shimmer.wrap(proto, 'makeBidiStreamRequest', createWrapMakeRequest(types.bidi, hasPeer))
@@ -4,7 +4,7 @@ const Module = require('module')
4
4
  const dc = require('dc-polyfill')
5
5
 
6
6
  const log = require('../../../dd-trace/src/log')
7
- const { loadChannel } = require('./register.js')
7
+ const { activate, loadChannel } = require('./register.js')
8
8
  const {
9
9
  filename,
10
10
  getDisabledInstrumentations,
@@ -96,7 +96,7 @@ dc.subscribe(CHANNEL, (message) => {
96
96
  if (disabledInstrumentations.has(name)) return
97
97
 
98
98
  if (payload.activate && isRewriteActivationEnabled(name)) {
99
- loadChannel.publish({ name })
99
+ activate(name, payload.version)
100
100
  return
101
101
  }
102
102