dd-trace 5.105.0 → 5.107.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 (108) hide show
  1. package/index.d.ts +20 -1
  2. package/package.json +5 -7
  3. package/packages/datadog-core/src/storage.js +47 -48
  4. package/packages/datadog-esbuild/index.js +6 -1
  5. package/packages/datadog-instrumentations/src/ai.js +12 -3
  6. package/packages/datadog-instrumentations/src/aws-sdk.js +3 -2
  7. package/packages/datadog-instrumentations/src/body-parser.js +5 -2
  8. package/packages/datadog-instrumentations/src/connect.js +3 -2
  9. package/packages/datadog-instrumentations/src/cookie-parser.js +3 -2
  10. package/packages/datadog-instrumentations/src/cucumber-worker-threads.js +19 -0
  11. package/packages/datadog-instrumentations/src/cucumber.js +319 -152
  12. package/packages/datadog-instrumentations/src/express-mongo-sanitize.js +7 -5
  13. package/packages/datadog-instrumentations/src/express-session.js +12 -11
  14. package/packages/datadog-instrumentations/src/express.js +24 -20
  15. package/packages/datadog-instrumentations/src/fastify.js +18 -6
  16. package/packages/datadog-instrumentations/src/helpers/openai-ai-guard.js +27 -12
  17. package/packages/datadog-instrumentations/src/http/client.js +9 -12
  18. package/packages/datadog-instrumentations/src/http/server.js +30 -16
  19. package/packages/datadog-instrumentations/src/http2/client.js +15 -12
  20. package/packages/datadog-instrumentations/src/http2/server.js +15 -8
  21. package/packages/datadog-instrumentations/src/jest/bail-reporter.js +42 -0
  22. package/packages/datadog-instrumentations/src/jest.js +143 -73
  23. package/packages/datadog-instrumentations/src/mocha/main.js +43 -8
  24. package/packages/datadog-instrumentations/src/mocha/utils.js +128 -17
  25. package/packages/datadog-instrumentations/src/multer.js +3 -2
  26. package/packages/datadog-instrumentations/src/mysql2.js +34 -0
  27. package/packages/datadog-instrumentations/src/net.js +8 -6
  28. package/packages/datadog-instrumentations/src/openai.js +19 -7
  29. package/packages/datadog-instrumentations/src/pg.js +19 -0
  30. package/packages/datadog-instrumentations/src/router.js +12 -10
  31. package/packages/datadog-instrumentations/src/vitest.js +29 -4
  32. package/packages/datadog-plugin-aws-sdk/src/base.js +0 -3
  33. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/tracing.js +1 -1
  34. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +218 -4
  35. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +62 -11
  36. package/packages/datadog-plugin-cucumber/src/index.js +2 -0
  37. package/packages/datadog-plugin-cypress/src/support.js +31 -1
  38. package/packages/datadog-plugin-http/src/client.js +0 -3
  39. package/packages/datadog-plugin-http/src/server.js +11 -1
  40. package/packages/datadog-plugin-mocha/src/index.js +2 -0
  41. package/packages/datadog-plugin-pg/src/index.js +10 -0
  42. package/packages/dd-trace/src/aiguard/index.js +34 -15
  43. package/packages/dd-trace/src/aiguard/sdk.js +34 -3
  44. package/packages/dd-trace/src/aiguard/tags.js +6 -0
  45. package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +1 -1
  46. package/packages/dd-trace/src/config/defaults.js +14 -0
  47. package/packages/dd-trace/src/config/generated-config-types.d.ts +1 -1
  48. package/packages/dd-trace/src/config/helper.js +1 -0
  49. package/packages/dd-trace/src/config/index.js +5 -9
  50. package/packages/dd-trace/src/config/parsers.js +8 -0
  51. package/packages/dd-trace/src/config/supported-configurations.json +13 -6
  52. package/packages/dd-trace/src/crashtracking/crashtracker.js +2 -2
  53. package/packages/dd-trace/src/datastreams/writer.js +1 -2
  54. package/packages/dd-trace/src/debugger/config.js +1 -1
  55. package/packages/dd-trace/src/debugger/devtools_client/config.js +3 -2
  56. package/packages/dd-trace/src/debugger/index.js +1 -2
  57. package/packages/dd-trace/src/dogstatsd.js +2 -3
  58. package/packages/dd-trace/src/encode/0.4.js +49 -41
  59. package/packages/dd-trace/src/encode/agentless-json.js +5 -1
  60. package/packages/dd-trace/src/encode/tags-processors.js +14 -0
  61. package/packages/dd-trace/src/exporters/agent/index.js +1 -2
  62. package/packages/dd-trace/src/exporters/agentless/index.js +6 -10
  63. package/packages/dd-trace/src/exporters/common/buffering-exporter.js +1 -2
  64. package/packages/dd-trace/src/exporters/common/request.js +26 -0
  65. package/packages/dd-trace/src/exporters/span-stats/index.js +1 -2
  66. package/packages/dd-trace/src/id.js +15 -0
  67. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +91 -5
  68. package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +43 -21
  69. package/packages/dd-trace/src/llmobs/plugins/genai/index.js +4 -0
  70. package/packages/dd-trace/src/llmobs/plugins/genai/util.js +45 -0
  71. package/packages/dd-trace/src/llmobs/sdk.js +4 -1
  72. package/packages/dd-trace/src/llmobs/span_processor.js +17 -1
  73. package/packages/dd-trace/src/llmobs/tagger.js +5 -3
  74. package/packages/dd-trace/src/llmobs/util.js +54 -0
  75. package/packages/dd-trace/src/llmobs/writers/base.js +1 -2
  76. package/packages/dd-trace/src/llmobs/writers/util.js +1 -2
  77. package/packages/dd-trace/src/openfeature/writers/base.js +1 -10
  78. package/packages/dd-trace/src/openfeature/writers/util.js +1 -2
  79. package/packages/dd-trace/src/opentelemetry/metrics/instruments.js +26 -13
  80. package/packages/dd-trace/src/opentelemetry/metrics/meter.js +7 -10
  81. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +92 -0
  82. package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +25 -5
  83. package/packages/dd-trace/src/opentracing/propagation/text_map.js +2 -10
  84. package/packages/dd-trace/src/opentracing/span.js +23 -18
  85. package/packages/dd-trace/src/opentracing/span_context.js +1 -3
  86. package/packages/dd-trace/src/opentracing/tracer.js +16 -12
  87. package/packages/dd-trace/src/plugins/ci_plugin.js +131 -46
  88. package/packages/dd-trace/src/priority_sampler.js +6 -5
  89. package/packages/dd-trace/src/profiling/config.js +11 -25
  90. package/packages/dd-trace/src/profiling/exporters/agent.js +11 -10
  91. package/packages/dd-trace/src/profiling/profiler.js +19 -9
  92. package/packages/dd-trace/src/profiling/profilers/wall.js +2 -3
  93. package/packages/dd-trace/src/proxy.js +13 -10
  94. package/packages/dd-trace/src/remote_config/index.js +1 -2
  95. package/packages/dd-trace/src/runtime_metrics/client.js +30 -0
  96. package/packages/dd-trace/src/runtime_metrics/index.js +12 -2
  97. package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +284 -0
  98. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +2 -11
  99. package/packages/dd-trace/src/service-naming/source-resolver.js +5 -1
  100. package/packages/dd-trace/src/span_format.js +33 -25
  101. package/packages/dd-trace/src/span_stats.js +1 -1
  102. package/packages/dd-trace/src/startup-log.js +1 -2
  103. package/packages/dd-trace/src/telemetry/send-data.js +1 -1
  104. package/packages/dd-trace/src/tracer.js +1 -1
  105. package/vendor/dist/@apm-js-collab/code-transformer/index.js +2 -2
  106. package/vendor/dist/shell-quote/index.js +1 -1
  107. package/packages/dd-trace/src/agent/url.js +0 -28
  108. package/scripts/preinstall.js +0 -34
@@ -18,57 +18,60 @@ const handleChannel = channel('apm:express:request:handle')
18
18
  const routeAddedChannel = channel('apm:express:route:added')
19
19
 
20
20
  function wrapHandle (handle) {
21
- return function handleWithTrace (req, res) {
21
+ return function handleWithTrace (...args) {
22
22
  if (handleChannel.hasSubscribers) {
23
- handleChannel.publish({ req })
23
+ handleChannel.publish({ req: args[0] })
24
24
  }
25
25
 
26
- return handle.apply(this, arguments)
26
+ return Reflect.apply(handle, this, args)
27
27
  }
28
28
  }
29
29
 
30
30
  const responseJsonChannel = channel('datadog:express:response:json:start')
31
31
 
32
32
  function wrapResponseJson (json) {
33
- return function wrappedJson (obj) {
33
+ return function wrappedJson (...args) {
34
34
  if (responseJsonChannel.hasSubscribers) {
35
- // backward compat as express 4.x supports deprecated 3.x signature
36
- if (arguments.length === 2 && typeof arguments[1] !== 'number') {
37
- obj = arguments[1]
35
+ let obj = args[0]
36
+ // Deprecated express 3.x res.json(status, body) form, still honored by 4.x but not
37
+ // exercised by any suite (the unit harness can't drive res.json's freshness path).
38
+ /* istanbul ignore if */
39
+ if (args.length === 2 && typeof args[1] !== 'number') {
40
+ obj = args[1]
38
41
  }
39
42
 
40
43
  responseJsonChannel.publish({ req: this.req, res: this, body: obj })
41
44
  }
42
45
 
43
- return json.apply(this, arguments)
46
+ return Reflect.apply(json, this, args)
44
47
  }
45
48
  }
46
49
 
47
50
  const responseRenderChannel = tracingChannel('datadog:express:response:render')
48
51
 
49
52
  function wrapResponseRender (render) {
50
- return function wrappedRender (view, options, callback) {
53
+ return function wrappedRender (...args) {
51
54
  if (!responseRenderChannel.start.hasSubscribers) {
52
- return render.apply(this, arguments)
55
+ return Reflect.apply(render, this, args)
53
56
  }
54
57
 
55
58
  const abortController = new AbortController()
56
59
  return responseRenderChannel.traceSync(
57
- function (...args) {
60
+ function (...renderArgs) {
58
61
  if (abortController.signal.aborted) {
59
62
  throw abortController.signal.reason || new Error('Aborted')
60
63
  }
61
64
 
62
- return render.apply(this, args)
65
+ return Reflect.apply(render, this, renderArgs)
63
66
  },
64
67
  {
65
68
  req: this.req,
66
- view,
67
- options,
69
+ view: args[0],
70
+ options: args[1],
68
71
  abortController,
69
72
  },
70
73
  this,
71
- ...arguments
74
+ ...args
72
75
  )
73
76
  }
74
77
  }
@@ -172,7 +175,8 @@ addHook({ name: 'express', versions: ['4'], file: 'lib/express.js' }, express =>
172
175
  const queryParserReadCh = channel('datadog:query:read:finish')
173
176
 
174
177
  function publishQueryParsedAndNext (req, res, next) {
175
- return shimmer.wrapFunction(next, next => function (...args) {
178
+ // Mirror next's name/arity so wrapCallback skips its per-call identity rewrite.
179
+ return shimmer.wrapCallback(next, original => function next (_error) {
176
180
  if (queryParserReadCh.hasSubscribers && req) {
177
181
  const abortController = new AbortController()
178
182
  const query = req.query
@@ -182,7 +186,7 @@ function publishQueryParsedAndNext (req, res, next) {
182
186
  if (abortController.signal.aborted) return
183
187
  }
184
188
 
185
- return next.apply(this, args)
189
+ return original.apply(this, arguments)
186
190
  })
187
191
  }
188
192
 
@@ -194,9 +198,9 @@ addHook({
194
198
  return shimmer.wrapFunction(query, query => function (...args) {
195
199
  const queryMiddleware = query.apply(this, args)
196
200
 
197
- return shimmer.wrapFunction(queryMiddleware, queryMiddleware => function (req, res, next) {
198
- arguments[2] = publishQueryParsedAndNext(req, res, next)
199
- return queryMiddleware.apply(this, arguments)
201
+ return shimmer.wrapFunction(queryMiddleware, queryMiddleware => function (...args) {
202
+ args[2] = publishQueryParsedAndNext(args[0], args[1], args[2])
203
+ return Reflect.apply(queryMiddleware, this, args)
200
204
  })
201
205
  })
202
206
  })
@@ -207,11 +207,14 @@ function preHandler (request, reply, done) {
207
207
 
208
208
  function preValidation (request, reply, done) {
209
209
  const req = getReq(request)
210
- const res = getRes(reply)
211
210
  const ctx = parsingContexts.get(req)
212
- ctx.res = res
213
211
 
214
- if (!ctx) return processInContext(request, ctx, done, req)
212
+ // No stored context means the onRequest/preParsing fast path ran (no error /
213
+ // cookie / callback subscribers), so there is nothing to publish on; forward
214
+ // `done` instead of dereferencing a missing ctx in processInContext.
215
+ if (!ctx) return done()
216
+
217
+ ctx.res = getRes(reply)
215
218
 
216
219
  preValidationCh.runStores(ctx, processInContext, undefined, request, ctx, done, req)
217
220
  }
@@ -276,7 +279,7 @@ function wrapSend (send, req) {
276
279
  const ctx = { req }
277
280
  if (payload instanceof Error) {
278
281
  ctx.error = payload
279
- errorChannel.publish(ctx)
282
+ publishError(ctx)
280
283
  } else if (canPublishResponsePayload(payload)) {
281
284
  const res = getRes(this)
282
285
  ctx.res = res
@@ -300,9 +303,18 @@ function getRouteConfig (request) {
300
303
  return request?.routeOptions?.config
301
304
  }
302
305
 
306
+ let publishingError = false
307
+
303
308
  function publishError (ctx) {
304
- if (ctx.error) {
305
- errorChannel.publish(ctx)
309
+ // `errorChannel` is public: a subscriber that re-enters the hook pipeline while
310
+ // handling the error republishes here and recurses until the stack overflows.
311
+ if (ctx.error && !publishingError) {
312
+ publishingError = true
313
+ try {
314
+ errorChannel.publish(ctx)
315
+ } finally {
316
+ publishingError = false
317
+ }
306
318
  }
307
319
 
308
320
  return ctx.error
@@ -15,8 +15,8 @@ const aiguardChannel = dc.channel('dd-trace:ai:aiguard')
15
15
  * @typedef {object} ResourceHandler
16
16
  * @property {(callArgs: object) => (Array<object>|undefined)} getInputMessages
17
17
  * @property {(body: object) => Array<object>} getOutputMessages
18
- * @property {(inputMessages: Array<object>, outputMessages: Array<object>) => Promise<unknown>}
19
- * publishOutputEvaluation
18
+ * @property {(inputMessages: Array<object>, outputMessages: Array<object>, parentSpan?: object)
19
+ * => Promise<unknown>} publishOutputEvaluation
20
20
  */
21
21
 
22
22
  /**
@@ -24,17 +24,29 @@ const aiguardChannel = dc.channel('dd-trace:ai:aiguard')
24
24
  * @property {ResourceHandler} handler
25
25
  * @property {Array<object>} inputMessages
26
26
  * @property {() => Promise<void>} getInputEval
27
+ * @property {object} [parentSpan] - LLM span (`openai.request`) to nest `ai_guard` spans under.
28
+ * Set by the instrumentation once the LLM span is active.
27
29
  */
28
30
 
29
31
  /**
30
32
  * Publishes already-converted AI-style messages to the AI Guard evaluation channel.
31
33
  *
34
+ * Subscribers push async work into `pending` and abort `abortController` to block.
35
+ *
32
36
  * @param {Array<object>} messages - AI-style messages to evaluate.
37
+ * @param {object} [parentSpan] - LLM span to use as the `ai_guard` span's parent.
33
38
  * @returns {Promise<void>}
34
39
  */
35
- function publishEvaluation (messages) {
36
- return new Promise((resolve, reject) => {
37
- aiguardChannel.publish({ messages, integration: 'openai', resolve, reject })
40
+ function publishEvaluation (messages, parentSpan) {
41
+ const abortController = new AbortController()
42
+ const ctx = { messages, integration: 'openai', parentSpan, abortController, pending: [] }
43
+
44
+ aiguardChannel.publish(ctx)
45
+
46
+ return Promise.all(ctx.pending).then(() => {
47
+ if (abortController.signal.aborted) {
48
+ throw abortController.signal.reason
49
+ }
38
50
  })
39
51
  }
40
52
 
@@ -84,12 +96,13 @@ function getChatCompletionsOutputMessages (body) {
84
96
  *
85
97
  * @param {Array<object>} inputMessages
86
98
  * @param {Array<object>} outputMessages - One entry per choice
99
+ * @param {object} [parentSpan]
87
100
  * @returns {Promise<Array<void>>}
88
101
  */
89
- function publishChatCompletionsOutputEvaluation (inputMessages, outputMessages) {
102
+ function publishChatCompletionsOutputEvaluation (inputMessages, outputMessages, parentSpan) {
90
103
  const evals = []
91
104
  for (const message of outputMessages) {
92
- evals.push(publishEvaluation([...inputMessages, message]))
105
+ evals.push(publishEvaluation([...inputMessages, message], parentSpan))
93
106
  }
94
107
  return Promise.all(evals)
95
108
  }
@@ -157,10 +170,11 @@ function getResponsesOutputMessages (body) {
157
170
  *
158
171
  * @param {Array<object>} inputMessages
159
172
  * @param {Array<object>} outputMessages
173
+ * @param {object} [parentSpan]
160
174
  * @returns {Promise<void>}
161
175
  */
162
- function publishResponsesOutputEvaluation (inputMessages, outputMessages) {
163
- return publishEvaluation([...inputMessages, ...outputMessages])
176
+ function publishResponsesOutputEvaluation (inputMessages, outputMessages, parentSpan) {
177
+ return publishEvaluation([...inputMessages, ...outputMessages], parentSpan)
164
178
  }
165
179
 
166
180
  /**
@@ -215,8 +229,9 @@ function createGuard (baseResource, callArgs, stream) {
215
229
  if (!inputMessages) return null
216
230
 
217
231
  let inputEvalPromise
218
- const getInputEval = () => (inputEvalPromise ??= publishEvaluation(inputMessages))
219
- return { handler, inputMessages, getInputEval }
232
+ const guard = { handler, inputMessages, parentSpan: undefined }
233
+ guard.getInputEval = () => (inputEvalPromise ??= publishEvaluation(inputMessages, guard.parentSpan))
234
+ return guard
220
235
  }
221
236
 
222
237
  /**
@@ -257,7 +272,7 @@ function gateParse (parsedPromise, guard) {
257
272
  function evaluateOutput (guard, body) {
258
273
  const outputMessages = guard.handler.getOutputMessages(body)
259
274
  if (!outputMessages.length) return Promise.resolve()
260
- return guard.handler.publishOutputEvaluation(guard.inputMessages, outputMessages)
275
+ return guard.handler.publishOutputEvaluation(guard.inputMessages, outputMessages, guard.parentSpan)
261
276
  }
262
277
 
263
278
  module.exports = {
@@ -219,7 +219,9 @@ function patch (http, methodName) {
219
219
  return setTimeout.apply(this, args)
220
220
  }
221
221
 
222
- req.emit = function (eventName, arg) {
222
+ req.emit = function (...args) {
223
+ const eventName = args[0]
224
+ const arg = args[1]
223
225
  switch (eventName) {
224
226
  case 'response': {
225
227
  const res = arg
@@ -233,7 +235,7 @@ function patch (http, methodName) {
233
235
  break
234
236
  }
235
237
 
236
- const result = emit.apply(this, arguments)
238
+ const result = Reflect.apply(emit, this, args)
237
239
 
238
240
  instrumentation.finalizeIfNeeded()
239
241
 
@@ -254,7 +256,7 @@ function patch (http, methodName) {
254
256
  finish()
255
257
  }
256
258
 
257
- return emit.apply(this, arguments)
259
+ return Reflect.apply(emit, this, args)
258
260
  }
259
261
 
260
262
  if (abortController.signal.aborted) {
@@ -292,17 +294,12 @@ function patch (http, methodName) {
292
294
 
293
295
  function normalizeOptions (inputURL) {
294
296
  if (typeof inputURL === 'string') {
295
- try {
296
- return urlToOptions(new url.URL(inputURL))
297
- } catch {
298
- // eslint-disable-next-line n/no-deprecated-api
299
- return url.parse(inputURL)
300
- }
301
- } else if (inputURL instanceof url.URL) {
297
+ return urlToOptions(new url.URL(inputURL))
298
+ }
299
+ if (inputURL instanceof url.URL) {
302
300
  return urlToOptions(inputURL)
303
- } else {
304
- return inputURL
305
301
  }
302
+ return inputURL
306
303
  }
307
304
 
308
305
  function urlToOptions (url) {
@@ -37,28 +37,35 @@ addHook({ name: 'https' }, http => {
37
37
  return http
38
38
  })
39
39
 
40
- function wrapResponseEmit (emit) {
41
- return function (eventName, event) {
40
+ function wrapResponseEmit (originalEmit) {
41
+ // Named `emit` mirrors the response method so the one-time prototype wrap
42
+ // skips its name rewrite; rest params keep the per-event forwarding
43
+ // allocation-free.
44
+ return function emit (...args) {
42
45
  if (!finishServerCh.hasSubscribers) {
43
- return emit.apply(this, arguments)
46
+ return Reflect.apply(originalEmit, this, args)
44
47
  }
45
48
 
49
+ const eventName = args[0]
46
50
  if ((eventName === 'finish' || eventName === 'close') && !requestFinishedSet.has(this)) {
47
51
  finishServerCh.publish({ req: this.req })
48
52
  requestFinishedSet.add(this)
49
53
  }
50
54
 
51
- return emit.apply(this, arguments)
55
+ return Reflect.apply(originalEmit, this, args)
52
56
  }
53
57
  }
54
58
 
55
- function wrapEmit (emit) {
56
- return function (eventName, req, res) {
59
+ function wrapEmit (originalEmit) {
60
+ return function emit (...args) {
57
61
  if (!startServerCh.hasSubscribers) {
58
- return emit.apply(this, arguments)
62
+ return Reflect.apply(originalEmit, this, args)
59
63
  }
60
64
 
65
+ const eventName = args[0]
61
66
  if (eventName === 'request') {
67
+ const req = args[1]
68
+ const res = args[2]
62
69
  res.req = req
63
70
 
64
71
  const abortController = new AbortController()
@@ -75,7 +82,7 @@ function wrapEmit (emit) {
75
82
  return this.listenerCount(eventName) > 0
76
83
  }
77
84
 
78
- return emit.apply(this, arguments)
85
+ return Reflect.apply(originalEmit, this, args)
79
86
  } catch (err) {
80
87
  errorServerCh.publish(err)
81
88
 
@@ -84,16 +91,23 @@ function wrapEmit (emit) {
84
91
  exitServerCh.publish(ctx)
85
92
  }
86
93
  }
87
- return emit.apply(this, arguments)
94
+ return Reflect.apply(originalEmit, this, args)
88
95
  }
89
96
  }
90
97
 
91
98
  function wrapWriteHead (writeHead) {
92
- return function wrappedWriteHead (statusCode, reason, obj) {
99
+ // Rest params + Reflect.apply instead of named formals + `arguments`: naming
100
+ // params while reading `arguments` makes V8 materialise the mapped arguments
101
+ // object on every call, including the no-subscriber fast path.
102
+ return function wrappedWriteHead (...args) {
93
103
  if (!startWriteHeadCh.hasSubscribers) {
94
- return writeHead.apply(this, arguments)
104
+ return Reflect.apply(writeHead, this, args)
95
105
  }
96
106
 
107
+ const statusCode = args[0]
108
+ const reason = args[1]
109
+ let obj = args[2]
110
+
97
111
  const abortController = new AbortController()
98
112
 
99
113
  if (typeof reason !== 'string') {
@@ -126,7 +140,7 @@ function wrapWriteHead (writeHead) {
126
140
  return this
127
141
  }
128
142
 
129
- return writeHead.apply(this, arguments)
143
+ return Reflect.apply(writeHead, this, args)
130
144
  }
131
145
  }
132
146
 
@@ -157,9 +171,9 @@ function wrapWrite (write) {
157
171
  }
158
172
 
159
173
  function wrapSetHeader (setHeader) {
160
- return function wrappedSetHeader (name, value) {
174
+ return function wrappedSetHeader (...args) {
161
175
  if (!startSetHeaderCh.hasSubscribers && !finishSetHeaderCh.hasSubscribers) {
162
- return setHeader.apply(this, arguments)
176
+ return Reflect.apply(setHeader, this, args)
163
177
  }
164
178
 
165
179
  if (startSetHeaderCh.hasSubscribers) {
@@ -171,10 +185,10 @@ function wrapSetHeader (setHeader) {
171
185
  }
172
186
  }
173
187
 
174
- const setHeaderResult = setHeader.apply(this, arguments)
188
+ const setHeaderResult = Reflect.apply(setHeader, this, args)
175
189
 
176
190
  if (finishSetHeaderCh.hasSubscribers) {
177
- finishSetHeaderCh.publish({ name, value, res: this })
191
+ finishSetHeaderCh.publish({ name: args[0], value: args[1], res: this })
178
192
  }
179
193
 
180
194
  return setHeaderResult
@@ -11,14 +11,16 @@ const asyncEndChannel = channel('apm:http2:client:request:asyncEnd')
11
11
  const errorChannel = channel('apm:http2:client:request:error')
12
12
 
13
13
  function createWrapEmit (ctx) {
14
- return function wrapEmit (emit) {
15
- return function (event, arg1) {
16
- ctx.eventName = event
17
- ctx.eventData = arg1
14
+ return function wrapEmit (originalEmit) {
15
+ // Named `emit`/arity-1 mirrors the request method so the per-request wrap
16
+ // skips its name/length rewrite.
17
+ return function emit (eventName) {
18
+ ctx.eventName = eventName
19
+ ctx.eventData = arguments[1]
18
20
 
19
21
  return asyncStartChannel.runStores(ctx, () => {
20
22
  try {
21
- return emit.apply(this, arguments)
23
+ return Reflect.apply(originalEmit, this, arguments)
22
24
  } finally {
23
25
  asyncEndChannel.publish(ctx)
24
26
  }
@@ -29,14 +31,14 @@ function createWrapEmit (ctx) {
29
31
 
30
32
  function createWrapRequest (authority, options) {
31
33
  return function wrapRequest (request) {
32
- return function (headers) {
33
- if (!startChannel.hasSubscribers) return request.apply(this, arguments)
34
+ return function (...args) {
35
+ if (!startChannel.hasSubscribers) return Reflect.apply(request, this, args)
34
36
 
35
- const ctx = { headers, authority, options }
37
+ const ctx = { headers: args[0], authority, options }
36
38
 
37
39
  return startChannel.runStores(ctx, () => {
38
40
  try {
39
- const req = request.apply(this, arguments)
41
+ const req = Reflect.apply(request, this, args)
40
42
 
41
43
  shimmer.wrap(req, 'emit', createWrapEmit(ctx))
42
44
 
@@ -54,13 +56,14 @@ function createWrapRequest (authority, options) {
54
56
  }
55
57
 
56
58
  function wrapConnect (connect) {
57
- return function (authority, options) {
59
+ return function (...args) {
60
+ const authority = args[0]
58
61
  if (connectChannel.hasSubscribers) {
59
62
  connectChannel.publish({ authority })
60
63
  }
61
- const session = connect.apply(this, arguments)
64
+ const session = Reflect.apply(connect, this, args)
62
65
 
63
- shimmer.wrap(session, 'request', createWrapRequest(authority, options))
66
+ shimmer.wrap(session, 'request', createWrapRequest(authority, args[1]))
64
67
 
65
68
  return session
66
69
  }
@@ -26,21 +26,28 @@ function wrapCreateServer (createServer) {
26
26
  }
27
27
  }
28
28
 
29
- function wrapResponseEmit (emit, ctx) {
30
- return function (eventName, event) {
29
+ function wrapResponseEmit (originalEmit, ctx) {
30
+ // Named `emit`/arity-1 mirrors the response method so the per-response wrap
31
+ // skips its name/length rewrite.
32
+ return function emit (eventName) {
31
33
  ctx.req = this.req
32
34
  ctx.eventName = eventName
33
- return emitCh.runStores(ctx, emit, this, ...arguments)
35
+ return emitCh.runStores(ctx, originalEmit, this, ...arguments)
34
36
  }
35
37
  }
36
38
 
37
- function wrapEmit (emit) {
38
- return function (eventName, req, res) {
39
+ function wrapEmit (originalEmit) {
40
+ // Named `emit` mirrors the server method so the one-time wrap skips its name
41
+ // rewrite; rest params keep the per-event forwarding allocation-free.
42
+ return function emit (...args) {
39
43
  if (!startServerCh.hasSubscribers) {
40
- return emit.apply(this, arguments)
44
+ return Reflect.apply(originalEmit, this, args)
41
45
  }
42
46
 
47
+ const eventName = args[0]
43
48
  if (eventName === 'request') {
49
+ const req = args[1]
50
+ const res = args[2]
44
51
  res.req = req
45
52
 
46
53
  const ctx = { req, res }
@@ -48,7 +55,7 @@ function wrapEmit (emit) {
48
55
  shimmer.wrap(res, 'emit', emit => wrapResponseEmit(emit, ctx))
49
56
 
50
57
  try {
51
- return emit.apply(this, arguments)
58
+ return Reflect.apply(originalEmit, this, args)
52
59
  } catch (error) {
53
60
  ctx.error = error
54
61
  errorServerCh.publish(ctx)
@@ -57,6 +64,6 @@ function wrapEmit (emit) {
57
64
  }
58
65
  })
59
66
  }
60
- return emit.apply(this, arguments)
67
+ return Reflect.apply(originalEmit, this, args)
61
68
  }
62
69
  }
@@ -0,0 +1,42 @@
1
+ 'use strict'
2
+
3
+ const JEST_SESSION_STATE = Symbol.for('dd-trace:jest:session')
4
+
5
+ class DatadogJestBailReporter {
6
+ /**
7
+ * @param {{ bail?: number, collectCoverage?: boolean, coverage?: boolean }} globalConfig
8
+ */
9
+ constructor (globalConfig) {
10
+ this.globalConfig = globalConfig
11
+ }
12
+
13
+ /**
14
+ * @param {Set<object>} _testContexts
15
+ * @param {{ numFailedTests?: number, numFailedTestSuites?: number, numRuntimeErrorTestSuites?: number }} results
16
+ * @returns {Promise<void> | void}
17
+ */
18
+ onRunComplete (_testContexts, results) {
19
+ const numBailFailures = getNumBailFailures(results)
20
+ if (
21
+ !this.globalConfig.bail ||
22
+ this.globalConfig.collectCoverage ||
23
+ this.globalConfig.coverage ||
24
+ numBailFailures < this.globalConfig.bail
25
+ ) {
26
+ return
27
+ }
28
+
29
+ return globalThis[JEST_SESSION_STATE]?.finishBailTestSession?.(results)
30
+ }
31
+ }
32
+
33
+ function getNumBailFailures (results) {
34
+ const numFailedTests = results?.numFailedTests || 0
35
+ const numFailedSuites = results?.numRuntimeErrorTestSuites === undefined
36
+ ? (numFailedTests === 0 ? results?.numFailedTestSuites || 0 : 0)
37
+ : results.numRuntimeErrorTestSuites
38
+
39
+ return numFailedTests + numFailedSuites
40
+ }
41
+
42
+ module.exports = DatadogJestBailReporter