dd-trace 5.102.0 → 5.103.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 (133) hide show
  1. package/ext/exporters.js +1 -0
  2. package/package.json +12 -11
  3. package/packages/datadog-esbuild/src/utils.js +2 -2
  4. package/packages/datadog-instrumentations/src/ai.js +1 -1
  5. package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +32 -15
  6. package/packages/datadog-instrumentations/src/couchbase.js +69 -220
  7. package/packages/datadog-instrumentations/src/cucumber.js +1 -1
  8. package/packages/datadog-instrumentations/src/electron/preload.js +42 -0
  9. package/packages/datadog-instrumentations/src/electron.js +240 -0
  10. package/packages/datadog-instrumentations/src/fetch.js +5 -5
  11. package/packages/datadog-instrumentations/src/graphql.js +13 -12
  12. package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +1 -1
  13. package/packages/datadog-instrumentations/src/helpers/hook.js +4 -1
  14. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  15. package/packages/datadog-instrumentations/src/helpers/instrument.js +2 -2
  16. package/packages/datadog-instrumentations/src/helpers/kafka.js +41 -0
  17. package/packages/datadog-instrumentations/src/ioredis.js +16 -12
  18. package/packages/datadog-instrumentations/src/jest.js +351 -50
  19. package/packages/datadog-instrumentations/src/kafkajs.js +164 -173
  20. package/packages/datadog-instrumentations/src/mocha/main.js +73 -1
  21. package/packages/datadog-instrumentations/src/mongodb-core.js +33 -8
  22. package/packages/datadog-instrumentations/src/pg.js +24 -10
  23. package/packages/datadog-instrumentations/src/playwright.js +427 -55
  24. package/packages/datadog-instrumentations/src/redis.js +19 -10
  25. package/packages/datadog-plugin-apollo/src/gateway/request.js +1 -21
  26. package/packages/datadog-plugin-aws-sdk/src/base.js +18 -24
  27. package/packages/datadog-plugin-aws-sdk/src/services/cloudwatchlogs.js +1 -1
  28. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +1 -1
  29. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -1
  30. package/packages/datadog-plugin-aws-sdk/src/services/lambda.js +1 -1
  31. package/packages/datadog-plugin-aws-sdk/src/services/redshift.js +1 -1
  32. package/packages/datadog-plugin-aws-sdk/src/services/s3.js +1 -1
  33. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +2 -2
  34. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -1
  35. package/packages/datadog-plugin-aws-sdk/src/services/stepfunctions.js +1 -1
  36. package/packages/datadog-plugin-couchbase/src/index.js +58 -52
  37. package/packages/datadog-plugin-cucumber/src/index.js +1 -0
  38. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +214 -22
  39. package/packages/datadog-plugin-cypress/src/support.js +13 -1
  40. package/packages/datadog-plugin-electron/src/index.js +17 -0
  41. package/packages/datadog-plugin-electron/src/ipc.js +143 -0
  42. package/packages/datadog-plugin-electron/src/net.js +82 -0
  43. package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +27 -18
  44. package/packages/datadog-plugin-graphql/src/execute.js +6 -28
  45. package/packages/datadog-plugin-graphql/src/resolve.js +30 -35
  46. package/packages/datadog-plugin-graphql/src/tools/signature.js +32 -7
  47. package/packages/datadog-plugin-graphql/src/tools/transforms.js +118 -100
  48. package/packages/datadog-plugin-graphql/src/utils.js +29 -0
  49. package/packages/datadog-plugin-grpc/src/client.js +6 -7
  50. package/packages/datadog-plugin-grpc/src/util.js +57 -22
  51. package/packages/datadog-plugin-http/src/client.js +2 -2
  52. package/packages/datadog-plugin-jest/src/index.js +92 -50
  53. package/packages/datadog-plugin-mocha/src/index.js +1 -0
  54. package/packages/datadog-plugin-mongodb-core/src/index.js +36 -70
  55. package/packages/datadog-plugin-mysql/src/index.js +1 -1
  56. package/packages/datadog-plugin-openai/src/services.js +2 -1
  57. package/packages/datadog-plugin-pg/src/index.js +3 -3
  58. package/packages/datadog-plugin-playwright/src/index.js +4 -0
  59. package/packages/datadog-plugin-redis/src/index.js +18 -23
  60. package/packages/dd-trace/src/aiguard/index.js +3 -1
  61. package/packages/dd-trace/src/aiguard/sdk.js +36 -30
  62. package/packages/dd-trace/src/aiguard/tags.js +20 -11
  63. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +2 -2
  64. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +1 -1
  65. package/packages/dd-trace/src/azure_metadata.js +17 -6
  66. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +4 -4
  67. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +4 -2
  68. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +6 -4
  69. package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +1 -1
  70. package/packages/dd-trace/src/config/defaults.js +3 -14
  71. package/packages/dd-trace/src/config/generated-config-types.d.ts +3 -1
  72. package/packages/dd-trace/src/config/helper.js +4 -0
  73. package/packages/dd-trace/src/config/index.js +2 -2
  74. package/packages/dd-trace/src/config/major-overrides.js +98 -0
  75. package/packages/dd-trace/src/config/parsers.js +7 -1
  76. package/packages/dd-trace/src/config/supported-configurations.json +51 -38
  77. package/packages/dd-trace/src/datastreams/checkpointer.js +2 -2
  78. package/packages/dd-trace/src/datastreams/manager.js +1 -1
  79. package/packages/dd-trace/src/datastreams/processor.js +2 -2
  80. package/packages/dd-trace/src/debugger/devtools_client/snapshot/collector.js +2 -2
  81. package/packages/dd-trace/src/debugger/devtools_client/source-maps.js +1 -1
  82. package/packages/dd-trace/src/debugger/devtools_client/state.js +2 -1
  83. package/packages/dd-trace/src/debugger/index.js +7 -7
  84. package/packages/dd-trace/src/dogstatsd.js +2 -2
  85. package/packages/dd-trace/src/encode/0.4.js +45 -54
  86. package/packages/dd-trace/src/encode/0.5.js +34 -3
  87. package/packages/dd-trace/src/encode/agentless-json.js +1 -1
  88. package/packages/dd-trace/src/exporter.js +2 -0
  89. package/packages/dd-trace/src/exporters/agent/index.js +2 -1
  90. package/packages/dd-trace/src/exporters/agentless/index.js +3 -2
  91. package/packages/dd-trace/src/exporters/agentless/writer.js +2 -2
  92. package/packages/dd-trace/src/exporters/common/buffering-exporter.js +2 -1
  93. package/packages/dd-trace/src/exporters/common/request.js +1 -1
  94. package/packages/dd-trace/src/exporters/electron/index.js +49 -0
  95. package/packages/dd-trace/src/external-logger/src/index.js +2 -1
  96. package/packages/dd-trace/src/git_metadata.js +10 -8
  97. package/packages/dd-trace/src/lambda/handler-paths.js +52 -0
  98. package/packages/dd-trace/src/lambda/index.js +62 -14
  99. package/packages/dd-trace/src/lambda/runtime/patch.js +21 -46
  100. package/packages/dd-trace/src/llmobs/index.js +13 -2
  101. package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +45 -15
  102. package/packages/dd-trace/src/llmobs/writers/base.js +2 -1
  103. package/packages/dd-trace/src/openfeature/writers/base.js +2 -1
  104. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +2 -1
  105. package/packages/dd-trace/src/opentracing/propagation/text_map.js +20 -9
  106. package/packages/dd-trace/src/payload-tagging/config/index.js +2 -2
  107. package/packages/dd-trace/src/plugins/ci_plugin.js +49 -4
  108. package/packages/dd-trace/src/plugins/database.js +54 -12
  109. package/packages/dd-trace/src/plugins/index.js +1 -0
  110. package/packages/dd-trace/src/plugins/plugin.js +2 -4
  111. package/packages/dd-trace/src/plugins/util/ci.js +8 -8
  112. package/packages/dd-trace/src/plugins/util/git-cache.js +20 -18
  113. package/packages/dd-trace/src/plugins/util/stacktrace.js +2 -2
  114. package/packages/dd-trace/src/plugins/util/test.js +37 -5
  115. package/packages/dd-trace/src/plugins/util/user-provided-git.js +17 -15
  116. package/packages/dd-trace/src/priority_sampler.js +1 -1
  117. package/packages/dd-trace/src/profiling/profiler.js +1 -1
  118. package/packages/dd-trace/src/profiling/profilers/wall.js +1 -1
  119. package/packages/dd-trace/src/profiling/ssi-heuristics.js +1 -1
  120. package/packages/dd-trace/src/rate_limiter.js +1 -1
  121. package/packages/dd-trace/src/remote_config/scheduler.js +1 -1
  122. package/packages/dd-trace/src/ritm.js +2 -1
  123. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +5 -8
  124. package/packages/dd-trace/src/serverless.js +5 -2
  125. package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +20 -0
  126. package/packages/dd-trace/src/service-naming/schemas/v0/web.js +4 -0
  127. package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +20 -0
  128. package/packages/dd-trace/src/service-naming/schemas/v1/web.js +4 -0
  129. package/packages/dd-trace/src/span_stats.js +1 -1
  130. package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
  131. package/packages/dd-trace/src/telemetry/endpoints.js +1 -1
  132. package/packages/dd-trace/src/telemetry/telemetry.js +2 -2
  133. package/packages/dd-trace/src/lambda/runtime/ritm.js +0 -133
@@ -0,0 +1,240 @@
1
+ 'use strict'
2
+
3
+ const { join } = require('path')
4
+ const { wrap } = require('../../datadog-shimmer')
5
+ const { addHook, channel, tracingChannel } = require('./helpers/instrument')
6
+
7
+ const requestCh = tracingChannel('apm:electron:net:request')
8
+ const mainReceiveCh = tracingChannel('apm:electron:ipc:main:receive')
9
+ const mainHandleCh = tracingChannel('apm:electron:ipc:main:handle')
10
+ const mainSendCh = tracingChannel('apm:electron:ipc:main:send')
11
+ const rendererPatchedCh = channel('apm:electron:ipc:renderer:patched')
12
+ const rendererReceiveCh = tracingChannel('apm:electron:ipc:renderer:receive')
13
+ const rendererSendCh = tracingChannel('apm:electron:ipc:renderer:send')
14
+
15
+ const listeners = {}
16
+ const handlers = {}
17
+
18
+ function createWrapRequest (ch) {
19
+ return function wrapRequest (request) {
20
+ return function (...args) {
21
+ if (!ch.start.hasSubscribers) return request.apply(this, arguments)
22
+
23
+ const ctx = { args }
24
+
25
+ return ch.start.runStores(ctx, () => {
26
+ try {
27
+ const req = request.apply(this, ctx.args)
28
+ const emit = req.emit
29
+
30
+ ctx.req = req
31
+
32
+ req.emit = function (eventName, arg) {
33
+ /* eslint-disable no-fallthrough */
34
+ switch (eventName) {
35
+ case 'response':
36
+ ctx.res = arg
37
+ ctx.res.on('error', error => {
38
+ ctx.error = error
39
+ ch.error.publish(ctx)
40
+ ch.asyncStart.publish(ctx)
41
+ })
42
+ ctx.res.on('end', () => ch.asyncStart.publish(ctx))
43
+ break
44
+ case 'error':
45
+ ctx.error = arg
46
+ ch.error.publish(ctx)
47
+ case 'abort':
48
+ ch.asyncStart.publish(ctx)
49
+ }
50
+
51
+ return emit.apply(this, arguments)
52
+ }
53
+
54
+ return req
55
+ } catch (e) {
56
+ ctx.error = e
57
+ ch.error.publish(ctx)
58
+ throw e
59
+ } finally {
60
+ ch.end.publish(ctx)
61
+ }
62
+ })
63
+ }
64
+ }
65
+ }
66
+
67
+ function createWrapAddListener (ch, mappings) {
68
+ return function wrapAddListener (addListener) {
69
+ return function (channel, listener) {
70
+ const wrappedListener = (event, ...args) => {
71
+ const ctx = { args, channel, event }
72
+
73
+ return ch.tracePromise(() => listener.call(this, event, ...args), ctx)
74
+ }
75
+
76
+ const mapping = mappings[channel] || new WeakMap()
77
+ const wrapper = mapping.get(listener) || wrappedListener
78
+
79
+ mapping.set(listener, wrapper)
80
+
81
+ return addListener.call(this, channel, wrappedListener)
82
+ }
83
+ }
84
+ }
85
+
86
+ function createWrapRemoveListener (mappings) {
87
+ return function wrapRemoveListener (removeListener) {
88
+ return function (channel, listener) {
89
+ const mapping = mappings[channel]
90
+
91
+ if (mapping) {
92
+ const wrapper = mapping.get(listener)
93
+
94
+ if (wrapper) {
95
+ return removeListener.call(this, channel, wrapper)
96
+ }
97
+ }
98
+
99
+ return removeListener.call(this, channel, listener)
100
+ }
101
+ }
102
+ }
103
+
104
+ function createWrapRemoveAllListeners (mappings) {
105
+ return function wrapRemoveAllListeners (removeAllListeners) {
106
+ return function (channel) {
107
+ if (channel) {
108
+ delete mappings[channel]
109
+ } else {
110
+ for (const key of Object.keys(mappings)) delete mappings[key]
111
+ }
112
+
113
+ return removeAllListeners.call(this, channel)
114
+ }
115
+ }
116
+ }
117
+
118
+ function createWrapSend (ch, promise = false) {
119
+ const trace = (promise ? ch.tracePromise : ch.traceSync).bind(ch)
120
+
121
+ return function wrapSend (send) {
122
+ return function (channel, ...args) {
123
+ const ctx = { args, channel, self: this }
124
+
125
+ return trace(() => send.call(this, channel, ...args), ctx)
126
+ }
127
+ }
128
+ }
129
+
130
+ function wrapSendToFrame (send) {
131
+ return function (frameId, channel, ...args) {
132
+ const ctx = { args, channel, frameId, self: this }
133
+
134
+ return mainSendCh.traceSync(() => send.call(this, frameId, channel, ...args), ctx)
135
+ }
136
+ }
137
+
138
+ function wrapBrowserWindow (electron) {
139
+ const moduleExports = {}
140
+
141
+ class DatadogBrowserWindow extends electron.BrowserWindow {
142
+ constructor (options = {}) {
143
+ const win = super(options)
144
+
145
+ win.webContents.session.registerPreloadScript({
146
+ type: 'frame', // TODO: service-worker
147
+ filePath: join(__dirname, 'electron', 'preload.js'),
148
+ })
149
+
150
+ // BrowserWindow doesn't support subclassing because it's all native code
151
+ // so we return an instance of it instead of the subclass.
152
+ return win
153
+ }
154
+ }
155
+
156
+ Object.defineProperty(moduleExports, 'BrowserWindow', {
157
+ enumerable: true,
158
+ get: () => DatadogBrowserWindow,
159
+ configurable: false,
160
+ })
161
+
162
+ for (const key of Reflect.ownKeys(electron)) {
163
+ const descriptor = Reflect.getOwnPropertyDescriptor(electron, key)
164
+
165
+ if (key === 'BrowserWindow') continue
166
+
167
+ Object.defineProperty(moduleExports, key, descriptor)
168
+ }
169
+
170
+ return moduleExports
171
+ }
172
+
173
+ function wrapWebContents (proto) {
174
+ const descriptor = Object.getOwnPropertyDescriptor(proto, 'webContents')
175
+ const wrapped = new WeakSet()
176
+ const wrapSend = createWrapSend(mainSendCh)
177
+
178
+ Object.defineProperty(proto, 'webContents', {
179
+ get () {
180
+ const webContents = descriptor.get.apply(this)
181
+
182
+ if (!wrapped.has(webContents)) {
183
+ // wrap(webContents, 'postMessage', wrapSend)
184
+ wrap(webContents, 'send', wrapSend)
185
+ wrap(webContents, 'sendToFrame', wrapSendToFrame)
186
+
187
+ wrapped.add(webContents)
188
+ }
189
+
190
+ return webContents
191
+ },
192
+ })
193
+ }
194
+
195
+ addHook({ name: 'electron', versions: ['>=37.0.0'] }, electron => {
196
+ // Electron exports a string in Node and an object in Electron.
197
+ if (typeof electron === 'string') return electron
198
+
199
+ const { BrowserWindow, ipcMain, ipcRenderer, net } = electron
200
+
201
+ if (net) {
202
+ // This also covers `fetch` as it uses `request` under the hood.
203
+ wrap(net, 'request', createWrapRequest(requestCh))
204
+ }
205
+
206
+ if (ipcRenderer) {
207
+ wrap(ipcRenderer, 'invoke', createWrapSend(rendererSendCh, true))
208
+ // wrap(ipcRenderer, 'postMessage', createWrapSend(rendererSendCh))
209
+ wrap(ipcRenderer, 'send', createWrapSend(rendererSendCh))
210
+ wrap(ipcRenderer, 'sendSync', createWrapSend(rendererSendCh))
211
+ wrap(ipcRenderer, 'sendToHost', createWrapSend(rendererSendCh))
212
+
213
+ wrap(ipcRenderer, 'addListener', createWrapAddListener(rendererReceiveCh, listeners))
214
+ wrap(ipcRenderer, 'off', createWrapRemoveListener(listeners))
215
+ wrap(ipcRenderer, 'on', createWrapAddListener(rendererReceiveCh, listeners))
216
+ wrap(ipcRenderer, 'once', createWrapAddListener(rendererReceiveCh, listeners))
217
+ wrap(ipcRenderer, 'removeListener', createWrapRemoveListener(listeners))
218
+ wrap(ipcRenderer, 'removeAllListeners', createWrapRemoveAllListeners(listeners))
219
+
220
+ ipcRenderer.send('datadog:apm:renderer:patched')
221
+ } else {
222
+ wrap(ipcMain, 'addListener', createWrapAddListener(mainReceiveCh, listeners))
223
+ wrap(ipcMain, 'handle', createWrapAddListener(mainHandleCh, handlers))
224
+ wrap(ipcMain, 'handleOnce', createWrapAddListener(mainHandleCh, handlers))
225
+ wrap(ipcMain, 'off', createWrapRemoveListener(listeners))
226
+ wrap(ipcMain, 'on', createWrapAddListener(mainReceiveCh, listeners))
227
+ wrap(ipcMain, 'once', createWrapAddListener(mainReceiveCh, listeners))
228
+ wrap(ipcMain, 'removeAllListeners', createWrapRemoveAllListeners(listeners))
229
+ wrap(ipcMain, 'removeHandler', createWrapRemoveAllListeners(handlers))
230
+ wrap(ipcMain, 'removeListener', createWrapRemoveListener(listeners))
231
+
232
+ ipcMain.once('datadog:apm:renderer:patched', event => rendererPatchedCh.publish(event))
233
+
234
+ wrapWebContents(BrowserWindow.prototype)
235
+
236
+ electron = wrapBrowserWindow(electron)
237
+ }
238
+
239
+ return electron
240
+ })
@@ -5,10 +5,10 @@ const { IS_SERVERLESS } = require('../../dd-trace/src/serverless')
5
5
  if (globalThis.fetch) {
6
6
  const globalFetch = globalThis.fetch
7
7
 
8
- let fetch = (input, init) => {
8
+ let wrappedFetch = (input, init) => {
9
9
  wrapRealFetch()
10
10
 
11
- return fetch(input, init)
11
+ return wrappedFetch(input, init)
12
12
  }
13
13
 
14
14
  function wrapRealFetch () {
@@ -20,14 +20,14 @@ if (globalThis.fetch) {
20
20
  channel('dd-trace:instrumentation:load').publish({ name: 'global:fetch' })
21
21
  })
22
22
 
23
- fetch = wrapFetch(globalFetch)
23
+ wrappedFetch = wrapFetch(globalFetch)
24
24
  }
25
25
 
26
26
  if (!IS_SERVERLESS) {
27
27
  wrapRealFetch()
28
28
  }
29
29
 
30
- globalThis.fetch = function value (input, init) {
31
- return fetch(input, init)
30
+ globalThis.fetch = function fetch (input, init) {
31
+ return wrappedFetch(input, init)
32
32
  }
33
33
  }
@@ -169,7 +169,7 @@ function wrapExecute (execute) {
169
169
  const ctx = {
170
170
  operation,
171
171
  args,
172
- docSource: documentSources.get(document),
172
+ docSource: source,
173
173
  source,
174
174
  fields: Object.create(null),
175
175
  abortController: new AbortController(),
@@ -257,13 +257,17 @@ function callInAsyncScope (fn, thisArg, args, abortController, cb) {
257
257
  }
258
258
 
259
259
  function pathToArray (path) {
260
- const flattened = []
261
- let curr = path
262
- while (curr) {
263
- flattened.push(curr.key)
264
- curr = curr.prev
260
+ let length = 0
261
+ for (let curr = path; curr; curr = curr.prev) {
262
+ length += 1
265
263
  }
266
- return flattened.reverse()
264
+
265
+ const flattened = new Array(length)
266
+ let index = length
267
+ for (let curr = path; curr; curr = curr.prev) {
268
+ flattened[--index] = curr.key
269
+ }
270
+ return flattened
267
271
  }
268
272
 
269
273
  function assertField (rootCtx, info, args) {
@@ -295,9 +299,7 @@ function wrapFields (type) {
295
299
 
296
300
  patchedTypes.add(type)
297
301
 
298
- for (const key of Object.keys(type._fields)) {
299
- const field = type._fields[key]
300
-
302
+ for (const field of Object.values(type._fields)) {
301
303
  wrapFieldResolve(field)
302
304
  wrapFieldType(field)
303
305
  }
@@ -321,8 +323,7 @@ function wrapFieldType (field) {
321
323
  }
322
324
 
323
325
  function finishResolvers ({ fields }) {
324
- for (const key of Object.keys(fields).reverse()) {
325
- const field = fields[key]
326
+ for (const field of Object.values(fields)) {
326
327
  field.ctx.finishTime = field.finishTime
327
328
  field.ctx.field = field
328
329
  if (field.error) {
@@ -19,7 +19,7 @@ const { channel } = require('./instrument')
19
19
  *
20
20
  * @param {string} prefix
21
21
  * @param {object} [options]
22
- * @param {boolean} [options.captureResult=false] set `ctx.result` to the callback's first
22
+ * @param {boolean} [options.captureResult] set `ctx.result` to the callback's first
23
23
  * non-error argument before publishing `:finish`. Plugins that tag spans from the call's
24
24
  * return value (e.g. the DNS lookup plugin) rely on this.
25
25
  * @returns {(buildContext: (thisArg: unknown, args: IArguments) => object | undefined) =>
@@ -16,7 +16,10 @@ function getVersion (moduleBaseDir) {
16
16
  return requirePackageJson(moduleBaseDir, /** @type {import('module').Module} */ (module)).version
17
17
  }
18
18
 
19
- return process.version
19
+ // In a packaged Electron binary, built-in modules (like 'electron', 'electron/main') have no
20
+ // moduleBaseDir. Use the Electron version for version checks when available, otherwise fall back
21
+ // to the Node.js version.
22
+ return process.versions?.electron ?? process.version
20
23
  }
21
24
 
22
25
  /**
@@ -68,6 +68,7 @@ module.exports = {
68
68
  cypress: () => require('../cypress'),
69
69
  'dd-trace-api': () => require('../dd-trace-api'),
70
70
  elasticsearch: () => require('../elasticsearch'),
71
+ electron: () => require('../electron'),
71
72
  express: () => require('../express'),
72
73
  'express-mongo-sanitize': () => require('../express-mongo-sanitize'),
73
74
  'express-session': () => require('../express-session'),
@@ -55,9 +55,9 @@ exports.getHooks = function getHooks (names) {
55
55
  * @param {object} args
56
56
  * @param {string} args.name module name
57
57
  * @param {string[]} [args.versions] array of semver range strings
58
- * @param {string} [args.file='index.js'] path to file within package to instrument
58
+ * @param {string} [args.file] path to file within package to instrument. Defaults to 'index.js'.
59
59
  * @param {string} [args.filePattern] pattern to match files within package to instrument
60
- * @param {boolean} [args.patchDefault=true] whether to patch the default export
60
+ * @param {boolean} [args.patchDefault] whether to patch the default export. Defaults to true.
61
61
  * @param {(moduleExports: unknown, version: string, isIitm?: boolean) => unknown} [hook] Patches module exports
62
62
  */
63
63
  exports.addHook = function addHook ({ name, versions, file, filePattern, patchDefault }, hook) {
@@ -0,0 +1,41 @@
1
+ 'use strict'
2
+
3
+ // Side-table mapping a kafkajs producer/consumer to the cluster captured at
4
+ // creation time. The boundary uses it to read `cluster.brokerPool` lazily on
5
+ // first send/consume instead of opening a parallel admin connection. A
6
+ // WeakMap keeps the kafkajs object itself untouched: no Symbol-keyed
7
+ // property to leak through `Reflect.ownKeys`, no string-keyed underscore for
8
+ // user serializers to pick up, and the entry drops as soon as the producer
9
+ // is GC'd.
10
+ const clientToCluster = new WeakMap()
11
+
12
+ /**
13
+ * Shallow-clone each message and its headers so the boundary, kafkajs, and
14
+ * the user never share the same nested objects. With `ensureHeaders` true
15
+ * (header injection enabled) messages without `headers` get an empty object
16
+ * the producer plugin can inject into; with it false (broker rejected
17
+ * headers) the absence of `headers` is preserved so brokers that fail on any
18
+ * header field can recover.
19
+ *
20
+ * @param {Array<unknown>} messages
21
+ * @param {boolean} ensureHeaders
22
+ */
23
+ function cloneMessages (messages, ensureHeaders) {
24
+ const result = new Array(messages.length)
25
+ for (let i = 0; i < messages.length; i++) {
26
+ const message = messages[i]
27
+ if (message === null || typeof message !== 'object') {
28
+ result[i] = message
29
+ } else if (message.headers) {
30
+ result[i] = { ...message, headers: { ...message.headers } }
31
+ } else {
32
+ result[i] = ensureHeaders ? { ...message, headers: {} } : { ...message }
33
+ }
34
+ }
35
+ return result
36
+ }
37
+
38
+ module.exports = {
39
+ clientToCluster,
40
+ cloneMessages,
41
+ }
@@ -10,6 +10,8 @@ const startCh = channel('apm:ioredis:command:start')
10
10
  const finishCh = channel('apm:ioredis:command:finish')
11
11
  const errorCh = channel('apm:ioredis:command:error')
12
12
 
13
+ const connectionOptionsCache = new WeakMap()
14
+
13
15
  function wrapRedis (Redis) {
14
16
  shimmer.wrap(Redis.prototype, 'sendCommand', sendCommand => function (command, stream) {
15
17
  if (!startCh.hasSubscribers) return sendCommand.apply(this, arguments)
@@ -17,21 +19,23 @@ function wrapRedis (Redis) {
17
19
  if (!command || !command.promise) return sendCommand.apply(this, arguments)
18
20
 
19
21
  const options = this.options || {}
20
- const connectionName = options.connectionName
21
- const db = options.db
22
- const connectionOptions = { host: options.host, port: options.port }
23
-
24
- const ctx = { db, command: command.name, args: command.args, connectionOptions, connectionName }
22
+ let connectionOptions = connectionOptionsCache.get(this)
23
+ if (connectionOptions === undefined) {
24
+ connectionOptions = { host: options.host, port: options.port }
25
+ connectionOptionsCache.set(this, connectionOptions)
26
+ }
27
+
28
+ const ctx = {
29
+ db: options.db,
30
+ command: command.name,
31
+ args: command.args,
32
+ connectionOptions,
33
+ connectionName: options.connectionName,
34
+ }
25
35
  return startCh.runStores(ctx, () => {
26
36
  command.promise.then(() => finish(finishCh, errorCh, ctx), err => finish(finishCh, errorCh, ctx, err))
27
37
 
28
- try {
29
- return sendCommand.apply(this, arguments)
30
- } catch (err) {
31
- errorCh.publish(err)
32
-
33
- throw err
34
- }
38
+ return sendCommand.apply(this, arguments)
35
39
  })
36
40
  })
37
41
  return Redis