dd-trace 5.104.0 → 5.106.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 (159) hide show
  1. package/LICENSE-3rdparty.csv +90 -102
  2. package/index.d.ts +82 -3
  3. package/package.json +15 -15
  4. package/packages/datadog-core/src/storage.js +1 -1
  5. package/packages/datadog-instrumentations/src/aerospike.js +1 -1
  6. package/packages/datadog-instrumentations/src/ai.js +8 -7
  7. package/packages/datadog-instrumentations/src/aws-sdk.js +16 -2
  8. package/packages/datadog-instrumentations/src/azure-cosmos.js +7 -0
  9. package/packages/datadog-instrumentations/src/azure-functions.js +3 -0
  10. package/packages/datadog-instrumentations/src/cucumber-worker-threads.js +19 -0
  11. package/packages/datadog-instrumentations/src/cucumber.js +390 -157
  12. package/packages/datadog-instrumentations/src/dns.js +54 -18
  13. package/packages/datadog-instrumentations/src/fastify.js +142 -82
  14. package/packages/datadog-instrumentations/src/graphql.js +188 -62
  15. package/packages/datadog-instrumentations/src/helpers/ai-messages.js +322 -14
  16. package/packages/datadog-instrumentations/src/helpers/hooks.js +4 -0
  17. package/packages/datadog-instrumentations/src/helpers/instrument.js +2 -1
  18. package/packages/datadog-instrumentations/src/helpers/openai-ai-guard.js +269 -0
  19. package/packages/datadog-instrumentations/src/helpers/promise-instrumentor.js +42 -0
  20. package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
  21. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +2 -3
  22. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/azure-cosmos.js +50 -0
  23. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +2 -0
  24. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/langgraph.js +4 -2
  25. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +85 -0
  26. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +37 -236
  27. package/packages/datadog-instrumentations/src/hono.js +54 -3
  28. package/packages/datadog-instrumentations/src/http/server.js +9 -4
  29. package/packages/datadog-instrumentations/src/jest/coverage-backfill.js +163 -0
  30. package/packages/datadog-instrumentations/src/jest.js +360 -150
  31. package/packages/datadog-instrumentations/src/kafkajs.js +120 -16
  32. package/packages/datadog-instrumentations/src/mocha/main.js +128 -17
  33. package/packages/datadog-instrumentations/src/nats.js +182 -0
  34. package/packages/datadog-instrumentations/src/nyc.js +38 -1
  35. package/packages/datadog-instrumentations/src/openai.js +33 -18
  36. package/packages/datadog-instrumentations/src/oracledb.js +6 -1
  37. package/packages/datadog-instrumentations/src/pino.js +17 -5
  38. package/packages/datadog-instrumentations/src/playwright.js +515 -292
  39. package/packages/datadog-instrumentations/src/router.js +76 -32
  40. package/packages/datadog-instrumentations/src/stripe.js +1 -1
  41. package/packages/datadog-plugin-avsc/src/schema_iterator.js +1 -1
  42. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/tracing.js +1 -1
  43. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +218 -4
  44. package/packages/datadog-plugin-azure-cosmos/src/index.js +144 -0
  45. package/packages/datadog-plugin-azure-event-hubs/src/producer.js +1 -1
  46. package/packages/datadog-plugin-azure-functions/src/index.js +5 -2
  47. package/packages/datadog-plugin-azure-service-bus/src/producer.js +1 -1
  48. package/packages/datadog-plugin-bunyan/src/index.js +28 -0
  49. package/packages/datadog-plugin-cucumber/src/index.js +17 -3
  50. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +199 -28
  51. package/packages/datadog-plugin-cypress/src/support.js +69 -1
  52. package/packages/datadog-plugin-dns/src/lookup.js +8 -6
  53. package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +1 -1
  54. package/packages/datadog-plugin-graphql/src/execute.js +2 -0
  55. package/packages/datadog-plugin-graphql/src/resolve.js +64 -67
  56. package/packages/datadog-plugin-http/src/server.js +40 -15
  57. package/packages/datadog-plugin-jest/src/index.js +11 -3
  58. package/packages/datadog-plugin-jest/src/util.js +15 -8
  59. package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +1 -1
  60. package/packages/datadog-plugin-kafkajs/src/producer.js +3 -0
  61. package/packages/datadog-plugin-langgraph/src/stream.js +1 -1
  62. package/packages/datadog-plugin-mocha/src/index.js +19 -4
  63. package/packages/datadog-plugin-mongodb-core/src/index.js +281 -40
  64. package/packages/datadog-plugin-nats/src/consumer.js +43 -0
  65. package/packages/datadog-plugin-nats/src/index.js +20 -0
  66. package/packages/datadog-plugin-nats/src/producer.js +62 -0
  67. package/packages/datadog-plugin-nats/src/util.js +33 -0
  68. package/packages/datadog-plugin-next/src/index.js +5 -3
  69. package/packages/datadog-plugin-openai/src/tracing.js +15 -2
  70. package/packages/datadog-plugin-oracledb/src/index.js +13 -2
  71. package/packages/datadog-plugin-pino/src/index.js +42 -0
  72. package/packages/datadog-plugin-playwright/src/index.js +4 -4
  73. package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +1 -1
  74. package/packages/datadog-plugin-rhea/src/producer.js +1 -1
  75. package/packages/datadog-plugin-router/src/index.js +33 -44
  76. package/packages/datadog-plugin-selenium/src/index.js +1 -1
  77. package/packages/datadog-plugin-vitest/src/index.js +5 -13
  78. package/packages/datadog-plugin-winston/src/index.js +30 -0
  79. package/packages/datadog-shimmer/src/shimmer.js +33 -40
  80. package/packages/dd-trace/src/aiguard/index.js +1 -1
  81. package/packages/dd-trace/src/aiguard/sdk.js +1 -1
  82. package/packages/dd-trace/src/appsec/api_security_sampler.js +1 -1
  83. package/packages/dd-trace/src/appsec/index.js +1 -1
  84. package/packages/dd-trace/src/appsec/reporter.js +5 -6
  85. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +1 -1
  86. package/packages/dd-trace/src/appsec/sdk/utils.js +1 -1
  87. package/packages/dd-trace/src/appsec/user_tracking.js +5 -4
  88. package/packages/dd-trace/src/baggage.js +7 -1
  89. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +0 -1
  90. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +25 -13
  91. package/packages/dd-trace/src/ci-visibility/test-optimization-cache.js +70 -6
  92. package/packages/dd-trace/src/config/generated-config-types.d.ts +6 -2
  93. package/packages/dd-trace/src/config/supported-configurations.json +27 -8
  94. package/packages/dd-trace/src/datastreams/writer.js +2 -4
  95. package/packages/dd-trace/src/debugger/devtools_client/condition.js +5 -8
  96. package/packages/dd-trace/src/encode/0.4.js +124 -108
  97. package/packages/dd-trace/src/encode/0.5.js +114 -26
  98. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +31 -23
  99. package/packages/dd-trace/src/encode/agentless-json.js +4 -2
  100. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +32 -13
  101. package/packages/dd-trace/src/encode/span-stats.js +16 -16
  102. package/packages/dd-trace/src/encode/tags-processors.js +16 -0
  103. package/packages/dd-trace/src/id.js +15 -0
  104. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +92 -6
  105. package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +43 -21
  106. package/packages/dd-trace/src/llmobs/plugins/genai/index.js +1 -1
  107. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js +1 -1
  108. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +9 -7
  109. package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
  110. package/packages/dd-trace/src/llmobs/plugins/openai/index.js +1 -1
  111. package/packages/dd-trace/src/llmobs/sdk.js +0 -16
  112. package/packages/dd-trace/src/llmobs/span_processor.js +3 -3
  113. package/packages/dd-trace/src/llmobs/tagger.js +9 -1
  114. package/packages/dd-trace/src/llmobs/telemetry.js +1 -1
  115. package/packages/dd-trace/src/llmobs/util.js +66 -3
  116. package/packages/dd-trace/src/log/index.js +1 -1
  117. package/packages/dd-trace/src/msgpack/chunk.js +394 -10
  118. package/packages/dd-trace/src/msgpack/index.js +96 -2
  119. package/packages/dd-trace/src/openfeature/encoding.js +70 -0
  120. package/packages/dd-trace/src/openfeature/flagging_provider.js +20 -0
  121. package/packages/dd-trace/src/openfeature/span-enrichment-hook.js +143 -0
  122. package/packages/dd-trace/src/openfeature/span-enrichment.js +149 -0
  123. package/packages/dd-trace/src/opentelemetry/span-helpers.js +4 -3
  124. package/packages/dd-trace/src/opentelemetry/span.js +1 -1
  125. package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +22 -3
  126. package/packages/dd-trace/src/opentracing/propagation/log.js +18 -7
  127. package/packages/dd-trace/src/opentracing/propagation/text_map.js +64 -77
  128. package/packages/dd-trace/src/opentracing/span.js +59 -19
  129. package/packages/dd-trace/src/opentracing/span_context.js +50 -3
  130. package/packages/dd-trace/src/plugins/ci_plugin.js +20 -20
  131. package/packages/dd-trace/src/plugins/database.js +7 -6
  132. package/packages/dd-trace/src/plugins/index.js +4 -0
  133. package/packages/dd-trace/src/plugins/log_injection.js +56 -0
  134. package/packages/dd-trace/src/plugins/log_plugin.js +3 -48
  135. package/packages/dd-trace/src/plugins/outbound.js +1 -1
  136. package/packages/dd-trace/src/plugins/plugin.js +15 -17
  137. package/packages/dd-trace/src/plugins/tracing.js +43 -5
  138. package/packages/dd-trace/src/plugins/util/test.js +236 -13
  139. package/packages/dd-trace/src/plugins/util/web.js +79 -65
  140. package/packages/dd-trace/src/priority_sampler.js +2 -2
  141. package/packages/dd-trace/src/profiling/config.js +10 -23
  142. package/packages/dd-trace/src/profiling/exporters/agent.js +11 -10
  143. package/packages/dd-trace/src/profiling/profiler.js +21 -11
  144. package/packages/dd-trace/src/profiling/profilers/wall.js +12 -7
  145. package/packages/dd-trace/src/sampling_rule.js +7 -7
  146. package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +10 -0
  147. package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +8 -0
  148. package/packages/dd-trace/src/service-naming/source-resolver.js +46 -0
  149. package/packages/dd-trace/src/span_format.js +190 -58
  150. package/packages/dd-trace/src/spanleak.js +1 -1
  151. package/packages/dd-trace/src/standalone/index.js +3 -3
  152. package/packages/dd-trace/src/tagger.js +0 -2
  153. package/vendor/dist/@apm-js-collab/code-transformer/index.js +70 -39
  154. package/vendor/dist/@datadog/sketches-js/LICENSE +10 -36
  155. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  156. package/vendor/dist/protobufjs/index.js +1 -1
  157. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  158. package/packages/dd-trace/src/msgpack/encoder.js +0 -308
  159. package/packages/dd-trace/src/plugins/structured_log_plugin.js +0 -9
@@ -3,6 +3,7 @@
3
3
  const shimmer = require('../../datadog-shimmer')
4
4
  const { addHook } = require('./helpers/instrument')
5
5
  const { createCallbackInstrumentor } = require('./helpers/callback-instrumentor')
6
+ const { createPromiseInstrumentor } = require('./helpers/promise-instrumentor')
6
7
 
7
8
  const rrtypes = {
8
9
  resolveAny: 'ANY',
@@ -18,30 +19,55 @@ const rrtypes = {
18
19
  resolveSoa: 'SOA',
19
20
  }
20
21
 
21
- addHook({ name: 'dns' }, dns => {
22
- const lookup = createCallbackInstrumentor('apm:dns:lookup', { captureResult: true })
23
- const lookupService = createCallbackInstrumentor('apm:dns:lookup_service', { captureResult: true })
24
- const resolve = createCallbackInstrumentor('apm:dns:resolve', { captureResult: true })
25
- const reverse = createCallbackInstrumentor('apm:dns:reverse', { captureResult: true })
26
-
27
- shimmer.wrap(dns, 'lookup', lookup(buildArgsContext()))
28
- shimmer.wrap(dns, 'lookupService', lookupService(buildArgsContext()))
29
- shimmer.wrap(dns, 'resolve', resolve(buildArgsContext()))
30
- shimmer.wrap(dns, 'reverse', reverse(buildArgsContext()))
31
-
32
- patchResolveShorthands(dns, resolve)
22
+ // `dns.promises` and `require('dns/promises')` resolve to the same exports object. Both
23
+ // access paths register a hook, so without a guard the second hook to fire would stack a
24
+ // second wrap layer on top and publish every `apm:dns:*` event twice per call. The WeakSet
25
+ // collapses the two hooks to one wrap regardless of which one runs first.
26
+ const wrappedPromiseApis = new WeakSet()
33
27
 
34
- if (dns.Resolver) {
35
- shimmer.wrap(dns.Resolver.prototype, 'resolve', resolve(buildArgsContext()))
36
- shimmer.wrap(dns.Resolver.prototype, 'reverse', reverse(buildArgsContext()))
28
+ addHook({ name: 'dns' }, dns => {
29
+ patchApi(dns, createCallbackInstrumentor, buildCallbackArgsContext)
37
30
 
38
- patchResolveShorthands(dns.Resolver.prototype, resolve)
31
+ if (dns.promises) {
32
+ patchPromiseApi(dns.promises)
39
33
  }
40
34
 
41
35
  return dns
42
36
  })
43
37
 
44
- function patchResolveShorthands (prototype, resolve) {
38
+ addHook({ name: 'dns/promises' }, dnsPromises => {
39
+ patchPromiseApi(dnsPromises)
40
+ return dnsPromises
41
+ })
42
+
43
+ function patchPromiseApi (api) {
44
+ if (wrappedPromiseApis.has(api)) return
45
+ wrappedPromiseApis.add(api)
46
+ patchApi(api, createPromiseInstrumentor, buildPromiseArgsContext)
47
+ }
48
+
49
+ function patchApi (api, instrumentorFactory, buildArgsContext) {
50
+ const lookup = instrumentorFactory('apm:dns:lookup', { captureResult: true })
51
+ const lookupService = instrumentorFactory('apm:dns:lookup_service', { captureResult: true })
52
+ const resolve = instrumentorFactory('apm:dns:resolve', { captureResult: true })
53
+ const reverse = instrumentorFactory('apm:dns:reverse', { captureResult: true })
54
+
55
+ shimmer.wrap(api, 'lookup', lookup(buildArgsContext()))
56
+ shimmer.wrap(api, 'lookupService', lookupService(buildArgsContext()))
57
+ shimmer.wrap(api, 'resolve', resolve(buildArgsContext()))
58
+ shimmer.wrap(api, 'reverse', reverse(buildArgsContext()))
59
+
60
+ patchResolveShorthands(api, resolve, buildArgsContext)
61
+
62
+ if (api.Resolver) {
63
+ shimmer.wrap(api.Resolver.prototype, 'resolve', resolve(buildArgsContext()))
64
+ shimmer.wrap(api.Resolver.prototype, 'reverse', reverse(buildArgsContext()))
65
+
66
+ patchResolveShorthands(api.Resolver.prototype, resolve, buildArgsContext)
67
+ }
68
+ }
69
+
70
+ function patchResolveShorthands (prototype, resolve, buildArgsContext) {
45
71
  for (const method of Object.keys(rrtypes)) {
46
72
  if (prototype[method]) {
47
73
  shimmer.wrap(prototype, method, resolve(buildArgsContext(rrtypes[method])))
@@ -49,7 +75,7 @@ function patchResolveShorthands (prototype, resolve) {
49
75
  }
50
76
  }
51
77
 
52
- function buildArgsContext (rrtype) {
78
+ function buildCallbackArgsContext (rrtype) {
53
79
  return function (_, args) {
54
80
  if (args.length < 2) return
55
81
  const captured = [...args]
@@ -60,3 +86,13 @@ function buildArgsContext (rrtype) {
60
86
  return { args: captured }
61
87
  }
62
88
  }
89
+
90
+ function buildPromiseArgsContext (rrtype) {
91
+ return function (_, args) {
92
+ const captured = [...args]
93
+ if (rrtype) {
94
+ captured.push(rrtype)
95
+ }
96
+ return { args: captured }
97
+ }
98
+ }
@@ -54,65 +54,119 @@ function wrapAddHook (addHook) {
54
54
 
55
55
  if (typeof fn !== 'function') return addHook.apply(this, arguments)
56
56
 
57
- arguments[arguments.length - 1] = shimmer.wrapFunction(fn, fn => function (request, reply, done) {
58
- const req = getReq(request)
59
- const ctx = { req }
60
-
61
- try {
62
- // done callback is always the last argument
63
- const doneCallback = arguments[arguments.length - 1]
64
-
65
- if (typeof doneCallback === 'function') {
66
- arguments[arguments.length - 1] = function (err) {
67
- ctx.error = err
68
- publishError(ctx)
69
-
70
- const hasCookies = request.cookies && Object.keys(request.cookies).length > 0
71
-
72
- if (cookieParserReadCh.hasSubscribers && hasCookies && !cookiesPublished.has(req)) {
73
- ctx.res = getRes(reply)
74
- ctx.abortController = new AbortController()
75
- ctx.cookies = request.cookies
76
-
77
- cookieParserReadCh.publish(ctx)
78
- cookiesPublished.add(req)
79
-
80
- if (ctx.abortController.signal.aborted) return
81
- }
82
-
83
- if (name === 'onRequest' || name === 'preParsing') {
84
- parsingContexts.set(req, ctx)
85
-
86
- return callbackFinishCh.runStores(ctx, () => {
87
- return doneCallback.apply(this, arguments)
88
- })
89
- }
90
- return doneCallback.apply(this, arguments)
91
- }
92
-
93
- return fn.apply(this, arguments)
94
- }
95
-
96
- const promise = fn.apply(this, arguments)
97
-
98
- if (promise && typeof promise.catch === 'function') {
99
- return promise.catch(err => {
100
- ctx.error = err
101
- return publishError(ctx)
102
- })
103
- }
104
-
105
- return promise
106
- } catch (e) {
107
- ctx.error = e
108
- throw publishError(ctx)
57
+ arguments[arguments.length - 1] = shimmer.wrapFunction(fn, fn => function wrappedHook () {
58
+ // Fast path: every fastify request invokes each addHook'd handler, so the wrap
59
+ // runs in the user's hot path. The only side effects this wrapper carries are
60
+ // the three channels below; when none of them have a subscriber (the default
61
+ // plugin config, and the steady state once appsec / cookie subscribers detach),
62
+ // the wrap has nothing to do, and a `fn.apply(this, arguments)` forward keeps
63
+ // V8's CallApplyArguments fast path intact.
64
+ //
65
+ // The previous shape mutated `arguments[arguments.length - 1]` to swap `done`.
66
+ // That mutation materialises the magical arguments object and disables V8
67
+ // inlining of the enclosing function. The slow path below builds a fresh args
68
+ // array instead so the hot fast path keeps a clean forward.
69
+ if (errorChannel.hasSubscribers || cookieParserReadCh.hasSubscribers || callbackFinishCh.hasSubscribers) {
70
+ return invokeHookWithContext(name, fn, this, arguments)
109
71
  }
72
+ return fn.apply(this, arguments)
110
73
  })
111
74
 
112
75
  return addHook.apply(this, arguments)
113
76
  })
114
77
  }
115
78
 
79
+ /**
80
+ * Slow path of {@link wrapAddHook}; entered only when at least one wrap-fed
81
+ * channel has a subscriber. Allocates the per-request context, rewraps `done`,
82
+ * and forwards to the user-supplied hook.
83
+ *
84
+ * @param {string} name Lifecycle phase the hook was registered against.
85
+ * @param {Function} fn User-supplied hook.
86
+ * @param {unknown} thisArg `this` Fastify passes to the hook.
87
+ * @param {ArrayLike<unknown>} args Fastify's positional args; the dispatcher always
88
+ * places `done` as the trailing positional (see fastify/lib/hooks.js hookIterator,
89
+ * onSendHookRunner, preParsingHookRunner, onRequestAbortHookRunner).
90
+ */
91
+ function invokeHookWithContext (name, fn, thisArg, args) {
92
+ const request = args[0]
93
+ const reply = args[1]
94
+ const req = getReq(request)
95
+ const ctx = { req }
96
+
97
+ try {
98
+ const lastArg = args[args.length - 1]
99
+
100
+ if (typeof lastArg === 'function') {
101
+ // Copy the args so we can swap the trailing `done` without touching the
102
+ // caller's magical arguments object. Fastify hook arities are 2 to 4
103
+ // across lifecycle phases, but `done` is always last.
104
+ const callArgs = [...args]
105
+ callArgs[callArgs.length - 1] = wrapHookDone(ctx, request, reply, req, name, lastArg)
106
+ return fn.apply(thisArg, callArgs)
107
+ }
108
+
109
+ const promise = fn.apply(thisArg, args)
110
+
111
+ if (promise && typeof promise.catch === 'function') {
112
+ return promise.catch(error => {
113
+ ctx.error = error
114
+ return publishError(ctx)
115
+ })
116
+ }
117
+
118
+ return promise
119
+ } catch (error) {
120
+ ctx.error = error
121
+ throw publishError(ctx)
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Per-request closure invoked when fastify resolves the user hook's `done`.
127
+ * Captures `ctx` plus the dispatcher-level fields needed to publish on the
128
+ * cookie / callback channels. The closure cannot be hoisted: fastify invokes
129
+ * `done` with a single `(err)` arg, so request / reply / req / name / doneCallback
130
+ * must close over rather than ride the call signature.
131
+ *
132
+ * @param {{ req: unknown, [key: string]: unknown }} ctx
133
+ * @param {{ cookies?: Record<string, unknown>, [key: string]: unknown }} request
134
+ * @param {object} reply
135
+ * @param {unknown} req
136
+ * @param {string} name
137
+ * @param {Function} doneCallback
138
+ */
139
+ function wrapHookDone (ctx, request, reply, req, name, doneCallback) {
140
+ return function wrappedDone (error) {
141
+ ctx.error = error
142
+ publishError(ctx)
143
+
144
+ const hasCookies = request.cookies && Object.keys(request.cookies).length > 0
145
+
146
+ if (cookieParserReadCh.hasSubscribers && hasCookies && !cookiesPublished.has(req)) {
147
+ ctx.res = getRes(reply)
148
+ ctx.abortController = new AbortController()
149
+ ctx.cookies = request.cookies
150
+
151
+ cookieParserReadCh.publish(ctx)
152
+ cookiesPublished.add(req)
153
+
154
+ if (ctx.abortController.signal.aborted) return
155
+ }
156
+
157
+ if (name === 'onRequest' || name === 'preParsing') {
158
+ parsingContexts.set(req, ctx)
159
+
160
+ if (callbackFinishCh.hasSubscribers) {
161
+ const self = this
162
+ const allArgs = arguments
163
+ return callbackFinishCh.runStores(ctx, () => doneCallback.apply(self, allArgs))
164
+ }
165
+ }
166
+ return doneCallback.apply(this, arguments)
167
+ }
168
+ }
169
+
116
170
  function onRequest (request, reply, done) {
117
171
  if (typeof done !== 'function') return
118
172
 
@@ -157,45 +211,51 @@ function preValidation (request, reply, done) {
157
211
  const ctx = parsingContexts.get(req)
158
212
  ctx.res = res
159
213
 
160
- const processInContext = () => {
161
- let abortController
214
+ if (!ctx) return processInContext(request, ctx, done, req)
162
215
 
163
- if (queryParamsReadCh.hasSubscribers && request.query) {
164
- abortController ??= new AbortController()
165
- ctx.abortController = abortController
166
- ctx.query = request.query
167
- queryParamsReadCh.publish(ctx)
168
-
169
- if (abortController.signal.aborted) return
170
- }
216
+ preValidationCh.runStores(ctx, processInContext, undefined, request, ctx, done, req)
217
+ }
171
218
 
172
- // Analyze body before schema validation
173
- if (bodyParserReadCh.hasSubscribers && request.body && !bodyPublished.has(req)) {
174
- abortController ??= new AbortController()
175
- ctx.abortController = abortController
176
- ctx.body = request.body
177
- bodyParserReadCh.publish(ctx)
219
+ /**
220
+ * @param {{ query?: object, body?: object, params?: object, [key: string]: unknown }} request
221
+ * @param {{ res?: object, abortController?: AbortController, [key: string]: unknown }} ctx
222
+ * @param {Function} done
223
+ * @param {unknown} req
224
+ */
225
+ function processInContext (request, ctx, done, req) {
226
+ let abortController
227
+
228
+ if (queryParamsReadCh.hasSubscribers && request.query) {
229
+ abortController ??= new AbortController()
230
+ ctx.abortController = abortController
231
+ ctx.query = request.query
232
+ queryParamsReadCh.publish(ctx)
233
+
234
+ if (abortController.signal.aborted) return
235
+ }
178
236
 
179
- bodyPublished.add(req)
237
+ // Analyze body before schema validation
238
+ if (bodyParserReadCh.hasSubscribers && request.body && !bodyPublished.has(req)) {
239
+ abortController ??= new AbortController()
240
+ ctx.abortController = abortController
241
+ ctx.body = request.body
242
+ bodyParserReadCh.publish(ctx)
180
243
 
181
- if (abortController.signal.aborted) return
182
- }
244
+ bodyPublished.add(req)
183
245
 
184
- if (pathParamsReadCh.hasSubscribers && request.params) {
185
- abortController ??= new AbortController()
186
- ctx.abortController = abortController
187
- ctx.params = request.params
188
- pathParamsReadCh.publish(ctx)
246
+ if (abortController.signal.aborted) return
247
+ }
189
248
 
190
- if (abortController.signal.aborted) return
191
- }
249
+ if (pathParamsReadCh.hasSubscribers && request.params) {
250
+ abortController ??= new AbortController()
251
+ ctx.abortController = abortController
252
+ ctx.params = request.params
253
+ pathParamsReadCh.publish(ctx)
192
254
 
193
- done()
255
+ if (abortController.signal.aborted) return
194
256
  }
195
257
 
196
- if (!ctx) return processInContext()
197
-
198
- preValidationCh.runStores(ctx, processInContext)
258
+ done()
199
259
  }
200
260
 
201
261
  function preParsing (request, reply, payload, done) {