dd-trace 6.7.0 → 6.9.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 (272) hide show
  1. package/ci/diagnose.js +27 -25
  2. package/ci/init.js +6 -6
  3. package/ci/runbook.md +114 -20
  4. package/ci/test-optimization-validation/approval-artifacts.js +36 -3
  5. package/ci/test-optimization-validation/approval.js +78 -22
  6. package/ci/test-optimization-validation/blocker-category.js +24 -0
  7. package/ci/test-optimization-validation/ci-discovery.js +23 -17
  8. package/ci/test-optimization-validation/ci-package-scripts.js +34 -6
  9. package/ci/test-optimization-validation/ci-remediation.js +1 -1
  10. package/ci/test-optimization-validation/cli.js +280 -52
  11. package/ci/test-optimization-validation/command-blocker.js +159 -20
  12. package/ci/test-optimization-validation/command-output-policy.js +14 -8
  13. package/ci/test-optimization-validation/command-runner.js +9 -7
  14. package/ci/test-optimization-validation/environment.js +4 -2
  15. package/ci/test-optimization-validation/executable.js +15 -13
  16. package/ci/test-optimization-validation/execution-lock.js +70 -0
  17. package/ci/test-optimization-validation/framework-adapters/cucumber-validation.json +3 -0
  18. package/ci/test-optimization-validation/framework-adapters/cucumber.js +657 -5
  19. package/ci/test-optimization-validation/framework-adapters/playwright.js +5 -5
  20. package/ci/test-optimization-validation/framework-adapters/vitest.js +517 -0
  21. package/ci/test-optimization-validation/generated-files.js +47 -37
  22. package/ci/test-optimization-validation/generated-test-contract.js +9 -7
  23. package/ci/test-optimization-validation/generated-verifier.js +24 -17
  24. package/ci/test-optimization-validation/literal-glob.js +52 -0
  25. package/ci/test-optimization-validation/manifest-loader.js +3 -0
  26. package/ci/test-optimization-validation/manifest-scaffold.js +301 -52
  27. package/ci/test-optimization-validation/manifest-schema.js +80 -17
  28. package/ci/test-optimization-validation/offline-fixtures.js +13 -4
  29. package/ci/test-optimization-validation/offline-output.js +6 -6
  30. package/ci/test-optimization-validation/package-check.js +94 -0
  31. package/ci/test-optimization-validation/plan-writer.js +183 -10
  32. package/ci/test-optimization-validation/preflight-runner.js +110 -31
  33. package/ci/test-optimization-validation/project-build-artifact.js +31 -0
  34. package/ci/test-optimization-validation/redaction.js +18 -27
  35. package/ci/test-optimization-validation/report-writer.js +182 -48
  36. package/ci/test-optimization-validation/result-semantics.js +20 -3
  37. package/ci/test-optimization-validation/runner-command.js +41 -21
  38. package/ci/test-optimization-validation/runner-contract.js +79 -43
  39. package/ci/test-optimization-validation/safe-files.js +4 -4
  40. package/ci/test-optimization-validation/scenarios/basic-reporting.js +60 -6
  41. package/ci/test-optimization-validation/scenarios/ci-wiring.js +141 -35
  42. package/ci/test-optimization-validation/scenarios/helpers.js +30 -6
  43. package/ci/test-optimization-validation/scenarios/test-management.js +2 -2
  44. package/ci/test-optimization-validation/static-diagnosis.js +10 -3
  45. package/ci/test-optimization-validation/test-output.js +2 -1
  46. package/ci/test-optimization-validation/validation-blocker.js +21 -0
  47. package/ci/vitest-no-worker-init-setup.mjs +377 -84
  48. package/index.d.ts +175 -14
  49. package/init.js +1 -17
  50. package/initialize.mjs +11 -0
  51. package/loader-hook.mjs +6 -4
  52. package/package.json +12 -4
  53. package/packages/datadog-core/src/utils/src/kebabcase.js +8 -2
  54. package/packages/datadog-core/src/utils/src/parse-tags.js +0 -1
  55. package/packages/datadog-esbuild/index.js +4 -2
  56. package/packages/datadog-esbuild/src/utils.js +17 -2
  57. package/packages/datadog-instrumentations/src/ai.js +61 -34
  58. package/packages/datadog-instrumentations/src/bluebird.js +6 -6
  59. package/packages/datadog-instrumentations/src/claude-agent-sdk.js +85 -9
  60. package/packages/datadog-instrumentations/src/cucumber.js +131 -51
  61. package/packages/datadog-instrumentations/src/dns.js +21 -2
  62. package/packages/datadog-instrumentations/src/electron.js +1 -0
  63. package/packages/datadog-instrumentations/src/fastify.js +3 -1
  64. package/packages/datadog-instrumentations/src/grpc/client.js +4 -2
  65. package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +3 -1
  66. package/packages/datadog-instrumentations/src/helpers/extract-package-and-module-path.js +10 -0
  67. package/packages/datadog-instrumentations/src/helpers/hook.js +19 -2
  68. package/packages/datadog-instrumentations/src/helpers/hooks.js +4 -0
  69. package/packages/datadog-instrumentations/src/helpers/register.js +9 -3
  70. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +34 -9
  71. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
  72. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +175 -0
  73. package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +20 -2
  74. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.js +32 -0
  75. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +61 -0
  76. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +50 -18
  77. package/packages/datadog-instrumentations/src/helpers/router-helper.js +38 -7
  78. package/packages/datadog-instrumentations/src/helpers/shared-utils.js +39 -0
  79. package/packages/datadog-instrumentations/src/http/client.js +1 -3
  80. package/packages/datadog-instrumentations/src/jest/coverage-backfill.js +1 -1
  81. package/packages/datadog-instrumentations/src/jest.js +986 -246
  82. package/packages/datadog-instrumentations/src/mocha/common.js +1 -3
  83. package/packages/datadog-instrumentations/src/mocha/main.js +109 -51
  84. package/packages/datadog-instrumentations/src/mocha/utils.js +120 -32
  85. package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +44 -0
  86. package/packages/datadog-instrumentations/src/mocha/worker.js +441 -43
  87. package/packages/datadog-instrumentations/src/moleculer/client.js +1 -1
  88. package/packages/datadog-instrumentations/src/mysql.js +1 -1
  89. package/packages/datadog-instrumentations/src/next.js +155 -23
  90. package/packages/datadog-instrumentations/src/openai.js +14 -0
  91. package/packages/datadog-instrumentations/src/pg.js +180 -6
  92. package/packages/datadog-instrumentations/src/playwright.js +176 -81
  93. package/packages/datadog-instrumentations/src/promise-js.js +4 -4
  94. package/packages/datadog-instrumentations/src/promise.js +3 -3
  95. package/packages/datadog-instrumentations/src/rhea.js +1 -1
  96. package/packages/datadog-instrumentations/src/router.js +195 -19
  97. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +567 -65
  98. package/packages/datadog-instrumentations/src/vitest-main.js +438 -54
  99. package/packages/datadog-instrumentations/src/vitest-util.js +42 -7
  100. package/packages/datadog-instrumentations/src/vitest-worker.js +234 -17
  101. package/packages/datadog-instrumentations/src/webdriverio.js +1090 -0
  102. package/packages/datadog-instrumentations/src/when.js +3 -3
  103. package/packages/datadog-instrumentations/src/ws.js +5 -1
  104. package/packages/datadog-plugin-aerospike/src/index.js +1 -3
  105. package/packages/datadog-plugin-amqplib/src/client.js +1 -1
  106. package/packages/datadog-plugin-amqplib/src/producer.js +1 -1
  107. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +59 -48
  108. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +2 -2
  109. package/packages/datadog-plugin-aws-sdk/src/services/stepfunctions.js +2 -2
  110. package/packages/datadog-plugin-azure-event-hubs/src/producer.js +1 -3
  111. package/packages/datadog-plugin-azure-service-bus/src/producer.js +5 -3
  112. package/packages/datadog-plugin-bullmq/src/producer.js +3 -3
  113. package/packages/datadog-plugin-child_process/src/index.js +3 -3
  114. package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +6 -11
  115. package/packages/datadog-plugin-cucumber/src/index.js +5 -1
  116. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +50 -46
  117. package/packages/datadog-plugin-cypress/src/source-map-utils.js +1 -1
  118. package/packages/datadog-plugin-cypress/src/support.js +7 -4
  119. package/packages/datadog-plugin-electron/src/net.js +10 -4
  120. package/packages/datadog-plugin-fastify/src/tracing.js +1 -1
  121. package/packages/datadog-plugin-fs/src/index.js +0 -4
  122. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +14 -7
  123. package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +19 -10
  124. package/packages/datadog-plugin-graphql/src/execute.js +1 -0
  125. package/packages/datadog-plugin-graphql/src/tools/signature.js +1 -1
  126. package/packages/datadog-plugin-http/src/client.js +3 -2
  127. package/packages/datadog-plugin-http2/src/client.js +1 -1
  128. package/packages/datadog-plugin-jest/src/index.js +3 -21
  129. package/packages/datadog-plugin-jest/src/util.js +4 -1
  130. package/packages/datadog-plugin-kafkajs/src/producer.js +2 -1
  131. package/packages/datadog-plugin-langchain/src/tokens.js +2 -2
  132. package/packages/datadog-plugin-mocha/src/index.js +342 -6
  133. package/packages/datadog-plugin-next/src/index.js +57 -9
  134. package/packages/datadog-plugin-openai/src/tracing.js +4 -0
  135. package/packages/datadog-plugin-pg/src/index.js +65 -1
  136. package/packages/datadog-plugin-playwright/src/index.js +1 -1
  137. package/packages/datadog-plugin-prisma/src/index.js +5 -1
  138. package/packages/datadog-plugin-rhea/src/consumer.js +16 -13
  139. package/packages/datadog-plugin-rhea/src/producer.js +1 -1
  140. package/packages/datadog-plugin-undici/src/index.js +2 -1
  141. package/packages/datadog-plugin-vitest/src/index.js +179 -18
  142. package/packages/datadog-shimmer/src/shimmer.js +6 -2
  143. package/packages/datadog-webpack/index.js +2 -2
  144. package/packages/dd-trace/src/aiguard/sdk.js +3 -0
  145. package/packages/dd-trace/src/appsec/{blocking.js → blocking/index.js} +26 -6
  146. package/packages/dd-trace/src/appsec/downstream_requests.js +1 -1
  147. package/packages/dd-trace/src/appsec/handlers/auth.js +62 -0
  148. package/packages/dd-trace/src/appsec/handlers/http-request.js +215 -0
  149. package/packages/dd-trace/src/appsec/handlers/http-response.js +78 -0
  150. package/packages/dd-trace/src/appsec/handlers/http-shared.js +24 -0
  151. package/packages/dd-trace/src/appsec/handlers/payments.js +104 -0
  152. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-base-analyzer.js +1 -1
  153. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-password-analyzer.js +1 -1
  154. package/packages/dd-trace/src/appsec/iast/analyzers/hsts-header-missing-analyzer.js +1 -1
  155. package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +1 -1
  156. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +1 -3
  157. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +2 -2
  158. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +2 -2
  159. package/packages/dd-trace/src/appsec/index.js +48 -476
  160. package/packages/dd-trace/src/appsec/sdk/track_event.js +10 -1
  161. package/packages/dd-trace/src/appsec/waf/waf_manager.js +1 -1
  162. package/packages/dd-trace/src/ci-visibility/coverage-report-discovery.js +46 -14
  163. package/packages/dd-trace/src/ci-visibility/efd-retry-policy.js +89 -0
  164. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/sink.js +7 -6
  165. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +29 -15
  166. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +19 -1
  167. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +25 -1
  168. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/webdriverio.js +26 -0
  169. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +9 -1
  170. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +22 -7
  171. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +9 -14
  172. package/packages/dd-trace/src/ci-visibility/requests/request.js +3 -3
  173. package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +21 -5
  174. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +2 -2
  175. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +5 -1
  176. package/packages/dd-trace/src/config/defaults.js +14 -6
  177. package/packages/dd-trace/src/config/generated-config-types.d.ts +4 -2
  178. package/packages/dd-trace/src/config/helper.js +2 -0
  179. package/packages/dd-trace/src/config/index.js +31 -5
  180. package/packages/dd-trace/src/config/parsers.js +19 -1
  181. package/packages/dd-trace/src/config/remote_config.js +1 -1
  182. package/packages/dd-trace/src/config/supported-configurations.json +13 -6
  183. package/packages/dd-trace/src/datastreams/encoding.js +3 -4
  184. package/packages/dd-trace/src/debugger/devtools_client/log.js +2 -1
  185. package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +1 -4
  186. package/packages/dd-trace/src/debugger/devtools_client/send.js +1 -2
  187. package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +2 -2
  188. package/packages/dd-trace/src/debugger/devtools_client/snapshot/redaction.js +16 -2
  189. package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -1
  190. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +0 -1
  191. package/packages/dd-trace/src/encode/tags-processors.js +2 -2
  192. package/packages/dd-trace/src/exporters/common/url.js +3 -5
  193. package/packages/dd-trace/src/exporters/electron/index.js +4 -1
  194. package/packages/dd-trace/src/external-logger/src/index.js +1 -2
  195. package/packages/dd-trace/src/flare/index.js +2 -2
  196. package/packages/dd-trace/src/guardrails/apply-pm2-env.js +16 -0
  197. package/packages/dd-trace/src/guardrails/log.js +1 -10
  198. package/packages/dd-trace/src/id.js +1 -1
  199. package/packages/dd-trace/src/llmobs/constants/tags.js +1 -1
  200. package/packages/dd-trace/src/llmobs/eval-metric.js +249 -0
  201. package/packages/dd-trace/src/llmobs/experiments/client.js +4 -2
  202. package/packages/dd-trace/src/llmobs/experiments/dataset.js +66 -14
  203. package/packages/dd-trace/src/llmobs/experiments/experiment.js +289 -95
  204. package/packages/dd-trace/src/llmobs/experiments/index.js +66 -19
  205. package/packages/dd-trace/src/llmobs/experiments/noop.js +95 -10
  206. package/packages/dd-trace/src/llmobs/experiments/result.js +13 -2
  207. package/packages/dd-trace/src/llmobs/experiments/util.js +220 -0
  208. package/packages/dd-trace/src/llmobs/noop.js +2 -0
  209. package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +1 -1
  210. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +1 -1
  211. package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +1 -1
  212. package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +5 -5
  213. package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/index.js +1 -1
  214. package/packages/dd-trace/src/llmobs/plugins/genai/index.js +1 -1
  215. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js +1 -1
  216. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +1 -1
  217. package/packages/dd-trace/src/llmobs/sdk.js +152 -78
  218. package/packages/dd-trace/src/llmobs/span_processor.js +9 -7
  219. package/packages/dd-trace/src/llmobs/tagger.js +1 -2
  220. package/packages/dd-trace/src/llmobs/telemetry.js +18 -0
  221. package/packages/dd-trace/src/llmobs/writers/base.js +3 -0
  222. package/packages/dd-trace/src/llmobs/writers/spans.js +12 -6
  223. package/packages/dd-trace/src/llmobs/writers/util.js +2 -0
  224. package/packages/dd-trace/src/log/channels.js +1 -9
  225. package/packages/dd-trace/src/log/levels.js +12 -0
  226. package/packages/dd-trace/src/noop/proxy.js +2 -2
  227. package/packages/dd-trace/src/openfeature/writers/exposures.js +1 -1
  228. package/packages/dd-trace/src/openfeature/writers/util.js +1 -1
  229. package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +1 -1
  230. package/packages/dd-trace/src/opentelemetry/metrics/meter.js +8 -4
  231. package/packages/dd-trace/src/opentelemetry/metrics/otlp_transformer.js +1 -1
  232. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +3 -2
  233. package/packages/dd-trace/src/opentelemetry/span.js +4 -0
  234. package/packages/dd-trace/src/opentracing/span.js +1 -1
  235. package/packages/dd-trace/src/opentracing/span_context.js +1 -1
  236. package/packages/dd-trace/src/plugins/ci_plugin.js +124 -23
  237. package/packages/dd-trace/src/plugins/index.js +2 -0
  238. package/packages/dd-trace/src/plugins/plugin.js +1 -1
  239. package/packages/dd-trace/src/plugins/tracing.js +1 -0
  240. package/packages/dd-trace/src/plugins/util/ci.js +13 -6
  241. package/packages/dd-trace/src/plugins/util/git.js +7 -5
  242. package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +3 -3
  243. package/packages/dd-trace/src/plugins/util/ip_extractor.js +1 -1
  244. package/packages/dd-trace/src/plugins/util/llm.js +2 -2
  245. package/packages/dd-trace/src/plugins/util/stacktrace.js +3 -0
  246. package/packages/dd-trace/src/plugins/util/tags.js +2 -0
  247. package/packages/dd-trace/src/plugins/util/test.js +356 -86
  248. package/packages/dd-trace/src/plugins/util/url.js +5 -5
  249. package/packages/dd-trace/src/plugins/util/user-provided-git.js +1 -1
  250. package/packages/dd-trace/src/plugins/util/web.js +24 -2
  251. package/packages/dd-trace/src/priority_sampler.js +6 -3
  252. package/packages/dd-trace/src/profiler.js +1 -1
  253. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +1 -1
  254. package/packages/dd-trace/src/profiling/profilers/events.js +2 -2
  255. package/packages/dd-trace/src/profiling/profilers/poisson.js +1 -1
  256. package/packages/dd-trace/src/profiling/profilers/shared.js +2 -2
  257. package/packages/dd-trace/src/proxy.js +1 -0
  258. package/packages/dd-trace/src/ritm.js +10 -20
  259. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +4 -2
  260. package/packages/dd-trace/src/sampling_rule.js +4 -2
  261. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +1 -1
  262. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +1 -1
  263. package/packages/dd-trace/src/startup-log.js +3 -0
  264. package/packages/dd-trace/src/telemetry/logs/index.js +3 -0
  265. package/packages/dd-trace/src/telemetry/logs/log-collector.js +3 -1
  266. package/packages/dd-trace/src/telemetry/telemetry.js +1 -1
  267. package/packages/dd-trace/src/util.js +16 -0
  268. package/vendor/dist/@apm-js-collab/code-transformer/index.js +12 -12
  269. package/vendor/dist/pprof-format/index.js +1 -1
  270. package/vendor/dist/source-map/index.js +1 -1
  271. package/version.js +10 -8
  272. /package/packages/dd-trace/src/appsec/{blocked_templates.js → blocking/templates.js} +0 -0
@@ -1,6 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const METHODS = [...require('http').METHODS.map(v => v.toLowerCase()), 'all']
4
+ const ROUTE_RESOLUTION_FAILED = Symbol('routeResolutionFailed')
4
5
  const shimmer = require('../../datadog-shimmer')
5
6
  const { addHook, channel, createErrorPublisher } = require('./helpers/instrument')
6
7
  const { getCompileToRegexp } = require('./path-to-regexp')
@@ -35,6 +36,7 @@ function isFastSlash (layer, matchers) {
35
36
  * @param {{ handle: Function, name?: string, path?: string,
36
37
  * regexp?: { fast_star?: boolean, fast_slash?: boolean } }} layer
37
38
  * @param {Array<{ path?: string, regex?: RegExp }> & { hasStarPath?: boolean, hasSlashPath?: boolean }} matchers
39
+ * @returns {void}
38
40
  */
39
41
  function annotateLayer (layer, matchers) {
40
42
  const handle = layer.handle
@@ -71,6 +73,25 @@ function resolveLayerRoute (meta, layer) {
71
73
  }
72
74
  }
73
75
 
76
+ /**
77
+ * Preserve the host Layer's error boundary while resolving a route outside its
78
+ * dispatch method. This path runs only for multi-pattern layers.
79
+ *
80
+ * @param {{ captureRoute?: string, needMultiMatch: boolean,
81
+ * matchers: Array<{ path?: string, regex?: RegExp }> }} meta
82
+ * @param {{ path?: string }} layer
83
+ * @param {Function} next
84
+ * @returns {string | undefined | typeof ROUTE_RESOLUTION_FAILED}
85
+ */
86
+ function resolveLayerRouteOrForwardError (meta, layer, next) {
87
+ try {
88
+ return resolveLayerRoute(meta, layer)
89
+ } catch (error) {
90
+ next(error)
91
+ return ROUTE_RESOLUTION_FAILED
92
+ }
93
+ }
94
+
74
95
  /**
75
96
  * Build the request/error dispatch wrappers for one host (`express` / `router`).
76
97
  * They wrap the layer's prototype dispatch and read the side-table metadata, so
@@ -79,6 +100,11 @@ function resolveLayerRoute (meta, layer) {
79
100
  * span is published only for the layer the host actually runs.
80
101
  *
81
102
  * @param {string} name Channel namespace (`apm:<name>:middleware:*`).
103
+ * @returns {{
104
+ * wrapLayerRequest: (originalRequest: Function) => Function,
105
+ * wrapLayerError: (originalError: Function) => Function,
106
+ * wrapLegacyHandle: (layer: object, original: Function, guardRepeatedNext?: boolean) => Function
107
+ * }}
82
108
  */
83
109
  function createLayerDispatchWrappers (name) {
84
110
  const enterChannel = channel(`apm:${name}:middleware:enter`)
@@ -128,6 +154,10 @@ function createLayerDispatchWrappers (name) {
128
154
  // `wrappedNext` through captures both without a tracer-side try/catch; only
129
155
  // `exit` needs the `finally`. express 4's native dispatch converts only the
130
156
  // synchronous throw — exactly what the pre-refactor handle wrap caught.
157
+ /**
158
+ * @param {Function} originalRequest
159
+ * @returns {Function}
160
+ */
131
161
  function wrapLayerRequest (originalRequest) {
132
162
  return function (req, res, next) {
133
163
  if (!enterChannel.hasSubscribers) return originalRequest.call(this, req, res, next)
@@ -135,8 +165,14 @@ function createLayerDispatchWrappers (name) {
135
165
  const meta = getLayerMeta(this)
136
166
  if (meta === undefined || this.handle.length > 3) return originalRequest.call(this, req, res, next)
137
167
 
168
+ let route = meta.captureRoute
169
+ if (meta.needMultiMatch) {
170
+ route = resolveLayerRouteOrForwardError(meta, this, next)
171
+ if (route === ROUTE_RESOLUTION_FAILED) return
172
+ }
173
+
138
174
  const wrappedNext = typeof next === 'function' ? wrapNext(req, meta.name, next) : next
139
- enterChannel.publish({ name: meta.name, req, route: resolveLayerRoute(meta, this), layer: this })
175
+ enterChannel.publish({ name: meta.name, req, route, layer: this })
140
176
 
141
177
  try {
142
178
  return originalRequest.call(this, req, res, wrappedNext)
@@ -146,6 +182,10 @@ function createLayerDispatchWrappers (name) {
146
182
  }
147
183
  }
148
184
 
185
+ /**
186
+ * @param {Function} originalError
187
+ * @returns {Function}
188
+ */
149
189
  function wrapLayerError (originalError) {
150
190
  return function (error, req, res, next) {
151
191
  if (!enterChannel.hasSubscribers) return originalError.call(this, error, req, res, next)
@@ -153,8 +193,14 @@ function createLayerDispatchWrappers (name) {
153
193
  const meta = getLayerMeta(this)
154
194
  if (meta === undefined || this.handle.length !== 4) return originalError.call(this, error, req, res, next)
155
195
 
196
+ let route = meta.captureRoute
197
+ if (meta.needMultiMatch) {
198
+ route = resolveLayerRouteOrForwardError(meta, this, next)
199
+ if (route === ROUTE_RESOLUTION_FAILED) return
200
+ }
201
+
156
202
  const wrappedNext = typeof next === 'function' ? wrapNext(req, meta.name, next) : next
157
- enterChannel.publish({ name: meta.name, req, route: resolveLayerRoute(meta, this), layer: this })
203
+ enterChannel.publish({ name: meta.name, req, route, layer: this })
158
204
 
159
205
  try {
160
206
  return originalError.call(this, error, req, res, wrappedNext)
@@ -164,36 +210,163 @@ function createLayerDispatchWrappers (name) {
164
210
  }
165
211
  }
166
212
 
167
- // express <4.6.0 has no `Layer` prototype dispatch: the router invokes
168
- // `layer.handle` directly and routes errors by its arity. There the handle is
169
- // replaced in place, with the arity preserved so the host still routes
170
- // correctly. Newer express, express 5, and the router package keep `handle`
171
- // pristine and are traced through the prototype wraps above.
172
- function wrapLegacyHandle (layer, original) {
213
+ // express <4.6.0 dispatches `layer.handle` directly, so its replacement must preserve arity.
214
+ /**
215
+ * @param {object} layer
216
+ * @param {Function} original
217
+ * @returns {Function}
218
+ */
219
+ function wrapNativeLegacyRequestHandle (layer, original) {
220
+ const meta = getLayerMeta(layer)
221
+ const { name, captureRoute, needMultiMatch } = meta
222
+ return shimmer.wrapFunction(original, inner => function (req, res, next) {
223
+ if (!enterChannel.hasSubscribers) return inner.call(this, req, res, next)
224
+
225
+ let calls = 0
226
+ if (typeof next === 'function') {
227
+ next = shimmer.wrapCallback(next, originalNext => function next (error) {
228
+ calls++
229
+ if (calls === 1) {
230
+ if (error && error !== 'route' && error !== 'router') {
231
+ publishError({ req, error })
232
+ }
233
+
234
+ nextChannel.publish({ req })
235
+ finishChannel.publish({ req })
236
+ } else if (calls === 2) {
237
+ repeatChannel.publish({ req, name, error })
238
+ }
239
+
240
+ originalNext.apply(this, arguments)
241
+ })
242
+ }
243
+
244
+ const route = needMultiMatch ? resolveLayerRoute(meta, layer) : captureRoute
245
+ enterChannel.publish({ name, req, route, layer })
246
+
247
+ try {
248
+ return inner.call(this, req, res, next)
249
+ } catch (error) {
250
+ if (calls === 0) {
251
+ calls = 1
252
+ publishError({ req, error })
253
+ nextChannel.publish({ req })
254
+ finishChannel.publish({ req })
255
+ }
256
+
257
+ throw error
258
+ } finally {
259
+ exitChannel.publish({ req })
260
+ }
261
+ })
262
+ }
263
+
264
+ /**
265
+ * @param {object} layer
266
+ * @param {Function} original
267
+ * @returns {Function}
268
+ */
269
+ function wrapNativeLegacyErrorHandle (layer, original) {
270
+ const meta = getLayerMeta(layer)
271
+ const { name, captureRoute, needMultiMatch } = meta
272
+ return shimmer.wrapFunction(original, inner => function (error, req, res, next) {
273
+ if (!enterChannel.hasSubscribers) return inner.call(this, error, req, res, next)
274
+
275
+ let calls = 0
276
+ if (typeof next === 'function') {
277
+ next = shimmer.wrapCallback(next, originalNext => function next (nextError) {
278
+ calls++
279
+ if (calls === 1) {
280
+ if (nextError && nextError !== 'route' && nextError !== 'router') {
281
+ publishError({ req, error: nextError })
282
+ }
283
+
284
+ nextChannel.publish({ req })
285
+ finishChannel.publish({ req })
286
+ } else if (calls === 2) {
287
+ repeatChannel.publish({ req, name, error: nextError })
288
+ }
289
+
290
+ originalNext.apply(this, arguments)
291
+ })
292
+ }
293
+
294
+ const route = needMultiMatch ? resolveLayerRoute(meta, layer) : captureRoute
295
+ enterChannel.publish({ name, req, route, layer })
296
+
297
+ try {
298
+ return inner.call(this, error, req, res, next)
299
+ } catch (caught) {
300
+ if (calls === 0) {
301
+ calls = 1
302
+ publishError({ req, error: caught })
303
+ nextChannel.publish({ req })
304
+ finishChannel.publish({ req })
305
+ }
306
+
307
+ throw caught
308
+ } finally {
309
+ exitChannel.publish({ req })
310
+ }
311
+ })
312
+ }
313
+
314
+ /**
315
+ * @param {object} layer
316
+ * @param {Function} original
317
+ * @param {boolean} [guardRepeatedNext]
318
+ * @returns {Function}
319
+ */
320
+ function wrapLegacyHandle (layer, original, guardRepeatedNext = false) {
321
+ if (!guardRepeatedNext) {
322
+ return original.length === 4
323
+ ? wrapNativeLegacyErrorHandle(layer, original)
324
+ : wrapNativeLegacyRequestHandle(layer, original)
325
+ }
326
+
173
327
  // `annotateLayer` always runs first in `wrapStack`, so the captured meta is
174
328
  // never undefined here (unlike the prototype wraps, where `this` can be any
175
329
  // layer the host dispatches).
176
330
  const meta = getLayerMeta(layer)
331
+ const { name, captureRoute, needMultiMatch } = meta
177
332
  const wrapped = shimmer.wrapFunction(original, inner => function (...args) {
178
333
  if (!enterChannel.hasSubscribers) return inner.apply(this, args)
179
334
 
180
335
  const isErrorHandler = original.length === 4
181
336
  const req = args[isErrorHandler ? 1 : 0]
182
337
  const nextIndex = isErrorHandler ? 3 : 2
183
- if (typeof args[nextIndex] === 'function') args[nextIndex] = wrapNext(req, meta.name, args[nextIndex])
338
+ let calls = 0
339
+ if (typeof args[nextIndex] === 'function') {
340
+ args[nextIndex] = shimmer.wrapCallback(args[nextIndex], originalNext => function next (error) {
341
+ calls++
342
+ if (calls === 1) {
343
+ if (error && error !== 'route' && error !== 'router') {
344
+ publishError({ req, error })
345
+ }
184
346
 
185
- enterChannel.publish({ name: meta.name, req, route: resolveLayerRoute(meta, layer), layer })
347
+ nextChannel.publish({ req })
348
+ finishChannel.publish({ req })
349
+ } else if (calls === 2) {
350
+ repeatChannel.publish({ req, name, error })
351
+ }
352
+
353
+ originalNext.apply(this, arguments)
354
+ })
355
+ }
356
+
357
+ const route = needMultiMatch ? resolveLayerRoute(meta, layer) : captureRoute
358
+ enterChannel.publish({ name, req, route, layer })
186
359
 
187
360
  try {
188
361
  return inner.apply(this, args)
189
362
  } catch (error) {
190
- // Unlike the prototype hosts, this router catches a synchronous throw
191
- // outside the layer and calls its own `next(error)`, never `wrappedNext`.
192
- // Mirror `wrapNext` here so the throwing layer still tags its error and
193
- // finishes, rather than lingering on the stack until request finish.
194
- publishError({ req, error })
195
- nextChannel.publish({ req })
196
- finishChannel.publish({ req })
363
+ // Legacy hosts catch outside the layer and never call its wrapped `next`, so finish before rethrowing.
364
+ if (calls === 0) {
365
+ calls = 1
366
+ publishError({ req, error })
367
+ nextChannel.publish({ req })
368
+ finishChannel.publish({ req })
369
+ }
197
370
 
198
371
  throw error
199
372
  } finally {
@@ -209,6 +382,7 @@ function createLayerDispatchWrappers (name) {
209
382
 
210
383
  /**
211
384
  * @param {{ handle_request?: unknown, handleRequest?: unknown }} layer
385
+ * @returns {boolean}
212
386
  */
213
387
  function hasLayerDispatch (layer) {
214
388
  return typeof layer.handle_request === 'function' || typeof layer.handleRequest === 'function'
@@ -221,9 +395,11 @@ function hasLayerDispatch (layer) {
221
395
  * Host-resolved path-to-regexp compile adapter, or undefined when the host
222
396
  * instance ships no path-to-regexp. Captured here so each express/router
223
397
  * instance keeps the dialect it actually loaded.
224
- * @param {((layer: object, original: Function) => Function) | undefined} [wrapLegacyHandle]
398
+ * @param {((layer: object, original: Function, guardRepeatedNext?: boolean) => Function) | undefined}
399
+ * [wrapLegacyHandle]
225
400
  * Fallback that replaces `layer.handle` for hosts without a `Layer` prototype
226
401
  * dispatch (express <4.6.0). Omitted for hosts that always ship one.
402
+ * @returns {(original: Function) => Function}
227
403
  */
228
404
  function createWrapRouterMethod (name, compile, wrapLegacyHandle) {
229
405
  const routeAddedChannel = channel(`apm:${name}:route:added`)
@@ -234,7 +410,7 @@ function createWrapRouterMethod (name, compile, wrapLegacyHandle) {
234
410
 
235
411
  if (wrapLegacyHandle !== undefined && !hasLayerDispatch(layer)) {
236
412
  if (layer.__handle) { // express-async-errors
237
- layer.__handle = wrapLegacyHandle(layer, layer.__handle)
413
+ layer.__handle = wrapLegacyHandle(layer, layer.__handle, true)
238
414
  } else {
239
415
  layer.handle = wrapLegacyHandle(layer, layer.handle)
240
416
  }