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
package/index.d.ts CHANGED
@@ -19,6 +19,12 @@ interface Tracer extends opentracing.Tracer {
19
19
 
20
20
  /**
21
21
  * Starts and returns a new Span representing a logical unit of work.
22
+ *
23
+ * The returned span is not activated on the current scope. Spans created
24
+ * while it is open — via {@link Tracer.trace} or auto-instrumentation — only
25
+ * nest under it when it is the active span, so wrap the work in
26
+ * {@link Scope.activate} (or use {@link Tracer.trace}) when child spans
27
+ * should descend from it.
22
28
  * @param {string} name The name of the operation.
23
29
  * @param {tracer.SpanOptions} [options] Options for the newly created span.
24
30
  * @returns {Span} A new Span object.
@@ -4000,6 +4006,14 @@ declare namespace tracer {
4000
4006
  template?: string | Message[]
4001
4007
  }
4002
4008
 
4009
+ interface ToolDefinition {
4010
+ name : string,
4011
+ description? : string,
4012
+ schema? : {[key : string] : any}
4013
+ version? : string
4014
+ }
4015
+
4016
+
4003
4017
  /**
4004
4018
  * Annotation options for LLM Observability spans.
4005
4019
  */
@@ -4046,6 +4060,12 @@ declare namespace tracer {
4046
4060
  * A Prompt object that represents the prompt used for an LLM call. Only used on `llm` spans.
4047
4061
  */
4048
4062
  prompt?: Prompt,
4063
+
4064
+ /**
4065
+ * A list of ToolDefinition object that represents the tools available to the LLM for this span
4066
+ * Each definition requires a `name` and optionally accepts `description`, `schema`, and `version`.
4067
+ * */
4068
+ toolDefinitions?: ToolDefinition[]
4049
4069
  }
4050
4070
 
4051
4071
  interface AnnotationContextOptions {
@@ -4161,7 +4181,6 @@ declare namespace tracer {
4161
4181
  */
4162
4182
  agentlessEnabled?: boolean,
4163
4183
  }
4164
-
4165
4184
  /** @hidden */
4166
4185
  type spanKind = 'agent' | 'workflow' | 'task' | 'tool' | 'retrieval' | 'embedding' | 'llm'
4167
4186
  }
package/package.json CHANGED
@@ -1,13 +1,12 @@
1
1
  {
2
2
  "name": "dd-trace",
3
- "version": "5.105.0",
3
+ "version": "5.107.0",
4
4
  "description": "Datadog APM tracing client for JavaScript",
5
5
  "main": "index.js",
6
6
  "typings": "index.d.ts",
7
7
  "scripts": {
8
8
  "env": "bash ./plugin-env",
9
9
  "prepare": "node scripts/patch-istanbul-lib-coverage.js && cd vendor && npm ci --include=dev",
10
- "preinstall": "node scripts/preinstall.js",
11
10
  "prepack": "node scripts/release/swap-v5-types.js",
12
11
  "bench": "node benchmark/index.js",
13
12
  "bench:e2e:test-optimization": "node benchmark/e2e-test-optimization/benchmark-run.js",
@@ -23,7 +22,7 @@
23
22
  "lint:fix": "node scripts/check_licenses.js && node scripts/check-no-coverage-artifacts.js && node scripts/check-no-mcr-images.js && node scripts/check-docker-image-shas.js && eslint . --concurrency=auto --max-warnings 0 --fix",
24
23
  "lint:inspect": "npx @eslint/config-inspector@latest",
25
24
  "lint:codeowners": "codeowners-audit",
26
- "lint:codeowners:ci": "codeowners-audit --glob='**/*.spec.js' --glob='benchmark/sirun/**'",
25
+ "lint:codeowners:ci": "codeowners-audit --glob='**/*.spec.js' --glob='benchmark/sirun/**' --glob='.agents/**' --glob='.claude/**'",
27
26
  "release:proposal": "node scripts/release/proposal",
28
27
  "services": "node ./scripts/install_plugin_modules && node packages/dd-trace/test/setup/services",
29
28
  "test": "echo '\nError: The root \"npm test\" command is intentionally disabled.\n\nInstead, run specific test suites:\n - npm run test:trace:core\n - npm run test:appsec\n - etc.\n\nOr run individual test files:\n npx mocha path/to/test.spec.js\n\nSee CONTRIBUTING.md (Testing section) for more details.\n' && exit 1",
@@ -153,7 +152,6 @@
153
152
  "packages/datadog-instrumentations/orchestrion.yml",
154
153
  "README.md",
155
154
  "register.js",
156
- "scripts/preinstall.js",
157
155
  "vendor/dist/**/*.d.ts",
158
156
  "vendor/dist/**/*.js",
159
157
  "vendor/dist/**/*.wasm",
@@ -166,11 +164,11 @@
166
164
  "opentracing": ">=0.14.7"
167
165
  },
168
166
  "optionalDependencies": {
169
- "@datadog/libdatadog": "0.9.3",
167
+ "@datadog/libdatadog": "0.9.4",
170
168
  "@datadog/native-appsec": "11.0.1",
171
169
  "@datadog/native-iast-taint-tracking": "4.2.0",
172
170
  "@datadog/native-metrics": "3.1.2",
173
- "@datadog/openfeature-node-server": "1.2.1",
171
+ "@datadog/openfeature-node-server": "2.0.0",
174
172
  "@datadog/pprof": "5.14.4",
175
173
  "@datadog/wasm-js-rewriter": "5.0.1",
176
174
  "@opentelemetry/api": ">=1.0.0 <1.10.0",
@@ -180,7 +178,7 @@
180
178
  "devDependencies": {
181
179
  "@actions/core": "^3.0.1",
182
180
  "@actions/github": "^9.1.1",
183
- "@babel/helpers": "^7.29.2",
181
+ "@babel/helpers": "^7.29.7",
184
182
  "@eslint/eslintrc": "^3.3.5",
185
183
  "@eslint/js": "^9.39.2",
186
184
  "@msgpack/msgpack": "^3.1.3",
@@ -3,37 +3,26 @@
3
3
  const { AsyncLocalStorage } = require('async_hooks')
4
4
 
5
5
  /**
6
- * This is exactly the same as AsyncLocalStorage, with the exception that it
7
- * uses a WeakMap to store the store object. This is because ALS stores the
8
- * store object as a property of the resource object, which causes all sorts
9
- * of problems with logging and memory. We substitute the `store` object with
10
- * a "handle" object, which is used as a key in a WeakMap, where the values
11
- * are the real store objects.
6
+ * `AsyncLocalStorage` with a `getHandle()` escape hatch: a span stashes the
7
+ * active handle at creation (see opentracing/span.js) so a later context can
8
+ * recover its store without holding the store itself.
9
+ *
10
+ * Under AsyncContextFrame Node's default ALS backend on every release that
11
+ * ships it — the active value lives in the context frame and is never written
12
+ * to the async resource, so the store is held directly and the handle is the
13
+ * store. Pre-ACF `async_hooks` instead pinned the value onto the resource
14
+ * object, where it was visible to logging and retained with the resource
15
+ * graph; the `!isACFActive` block below restores the original WeakMap-handle
16
+ * indirection that kept the real store off the resource on those runtimes.
12
17
  *
13
18
  * @template T
14
19
  * @typedef {Record<string, T>} Store
15
20
  */
16
21
  class DatadogStorage extends AsyncLocalStorage {
17
22
  /**
18
- * @param {Store<unknown>} [store]
19
- * @override
20
- */
21
- enterWith (store) {
22
- const handle = { noop: store?.noop }
23
- stores.set(handle, store)
24
- super.enterWith(handle)
25
- }
26
-
27
- /**
28
- * This is method is a passthrough to the real `getStore()`, so that, when we
29
- * need it, we can use the handle rather than our mapped store.
30
- *
31
- * It's only here because stores are currently used for a bunch of things,
32
- * and we don't want to hold on to all of them in spans
33
- * (see opentracing/span.js). Using a namespaced storage for spans would
34
- * solve this.
35
- *
36
- * TODO: Refactor the Scope class to use a span-only store and remove this.
23
+ * Passthrough to the real `getStore()`. A span stashes this handle and feeds
24
+ * it back to `getStore(handle)` later. Identical in both modes: under ACF the
25
+ * handle is the store; without ACF it is the WeakMap key.
37
26
  *
38
27
  * @returns {Store<unknown>}
39
28
  */
@@ -42,22 +31,13 @@ class DatadogStorage extends AsyncLocalStorage {
42
31
  }
43
32
 
44
33
  /**
45
- * Here, we replicate the behavior of the original `getStore()` method by
46
- * passing in the handle, which we retrieve by calling it on super. Handles
47
- * retrieved through `getHandle()` can also be passed in to be used as the
48
- * key. This is useful if you've stashed a handle somewhere and want to
49
- * retrieve the store with it.
50
- * @param {object} [handle]
34
+ * @param {Store<unknown>} [handle] A handle from `getHandle()`; defaults to
35
+ * the active one. Under ACF the handle is the store, so it is returned as-is.
51
36
  * @returns {Store<unknown> | undefined}
52
37
  * @override
53
38
  */
54
39
  getStore (handle) {
55
- if (!handle) {
56
- handle = super.getStore()
57
- }
58
- if (handle) {
59
- return stores.get(handle)
60
- }
40
+ return handle ?? super.getStore()
61
41
  }
62
42
  }
63
43
 
@@ -77,12 +57,37 @@ if (!isACFActive) {
77
57
  const superGetStore = AsyncLocalStorage.prototype.getStore
78
58
  const superEnterWith = AsyncLocalStorage.prototype.enterWith
79
59
 
60
+ // Without ACF, ALS writes the entered value onto the async resource. Keep the
61
+ // real store off the resource by entering a small handle and mapping it to
62
+ // the store through a WeakMap, then reversing the lookup on read.
63
+ const stores = new WeakMap()
64
+
80
65
  /**
81
- * Override the `run` method to manually call `enterWith` and `getStore`
82
- * when not using AsyncContextFrame.
83
- *
84
- * Without ACF, super.run() won't call this.enterWith(), so the WeakMap handle
85
- * is never created and getStore() would fail.
66
+ * @param {Store<unknown>} [store]
67
+ */
68
+ DatadogStorage.prototype.enterWith = function enterWith (store) {
69
+ const handle = { noop: store?.noop }
70
+ stores.set(handle, store)
71
+ superEnterWith.call(this, handle)
72
+ }
73
+
74
+ /**
75
+ * @param {object} [handle]
76
+ * @returns {Store<unknown> | undefined}
77
+ */
78
+ DatadogStorage.prototype.getStore = function getStore (handle) {
79
+ if (!handle) {
80
+ handle = superGetStore.call(this)
81
+ }
82
+ if (handle) {
83
+ return stores.get(handle)
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Without ACF, `super.run()` does not delegate to `enterWith()`, so the
89
+ * WeakMap handle is never created and `getStore()` would miss. Drive the
90
+ * handle path manually and restore the prior handle on the way out.
86
91
  *
87
92
  * @template R
88
93
  * @template {unknown[]} TArgs
@@ -103,12 +108,6 @@ if (!isACFActive) {
103
108
  }
104
109
  }
105
110
 
106
- /**
107
- * This is the map from handles to real stores, used in the class above.
108
- * @type {WeakMap<WeakKey, Store<unknown>|undefined>}
109
- */
110
- const stores = new WeakMap()
111
-
112
111
  /**
113
112
  * For convenience, we use the `storage` function as a registry of namespaces
114
113
  * corresponding to DatadogStorage instances. This lets us have separate
@@ -347,10 +347,15 @@ register(${JSON.stringify(toRegister)}, _, set, get, ${JSON.stringify(data.raw)}
347
347
  }
348
348
  } else {
349
349
  const fileCode = fs.readFileSync(args.path, 'utf8')
350
+ // Don't spread `...arguments`: esbuild's minifier can rewrite the surrounding
351
+ // `__commonJS` factory into an arrow function whose `arguments` resolves to the
352
+ // ESM top-level scope (see issue #8681). Pass `(module.exports, module)`
353
+ // explicitly; the IIFE declares no parameters so esbuild's static `require()`
354
+ // resolution inside `fileCode` is preserved through the factory's closure.
350
355
  contents = `
351
356
  (function() {
352
357
  ${fileCode}
353
- })(...arguments);
358
+ })(module.exports, module);
354
359
  {
355
360
  const dc = require('dc-polyfill');
356
361
  const ch = dc.channel('${CHANNEL}');
@@ -2,8 +2,8 @@
2
2
 
3
3
  const { channel, tracingChannel } = require('dc-polyfill')
4
4
  const shimmer = require('../../datadog-shimmer')
5
- const { addHook, getHooks } = require('./helpers/instrument')
6
5
  const { convertVercelPromptToMessages, buildOutputMessages } = require('./helpers/ai-messages')
6
+ const { addHook, getHooks } = require('./helpers/instrument')
7
7
 
8
8
  const vercelAiTracingChannel = tracingChannel('dd-trace:vercel-ai')
9
9
  const vercelAiSpanSetAttributesChannel = channel('dd-trace:vercel-ai:span:setAttributes')
@@ -15,12 +15,21 @@ const wrappedModels = new WeakSet()
15
15
  /**
16
16
  * Publishes already-converted AI-style messages to the AI Guard evaluation channel.
17
17
  *
18
+ * Subscribers push async work into `pending` and abort `abortController` to block.
19
+ *
18
20
  * @param {Array<object>} messages - AI-style messages to evaluate.
19
21
  * @returns {Promise<void>}
20
22
  */
21
23
  function publishEvaluation (messages) {
22
- return new Promise((resolve, reject) => {
23
- aiguardChannel.publish({ messages, integration: 'ai', resolve, reject })
24
+ const abortController = new AbortController()
25
+ const ctx = { messages, integration: 'ai', abortController, pending: [] }
26
+
27
+ aiguardChannel.publish(ctx)
28
+
29
+ return Promise.all(ctx.pending).then(() => {
30
+ if (abortController.signal.aborted) {
31
+ throw abortController.signal.reason
32
+ }
24
33
  })
25
34
  }
26
35
 
@@ -233,13 +233,14 @@ function wrapSmithySend (send) {
233
233
  }
234
234
 
235
235
  function handleCompletion (result, ctx, channels) {
236
- const iterator = result?.body?.[Symbol.asyncIterator]
236
+ const streamable = result?.body ?? result?.stream
237
+ const iterator = streamable?.[Symbol.asyncIterator]
237
238
  if (!iterator) {
238
239
  channels.complete.publish(ctx)
239
240
  return
240
241
  }
241
242
 
242
- shimmer.wrap(result.body, Symbol.asyncIterator, function (asyncIterator) {
243
+ shimmer.wrap(streamable, Symbol.asyncIterator, function (asyncIterator) {
243
244
  return function (...args) {
244
245
  const iterator = asyncIterator.apply(this, args)
245
246
  shimmer.wrap(iterator, 'next', function (next) {
@@ -6,7 +6,10 @@ const { channel, addHook, AsyncResource } = require('./helpers/instrument')
6
6
  const bodyParserReadCh = channel('datadog:body-parser:read:finish')
7
7
 
8
8
  function publishRequestBodyAndNext (req, res, next) {
9
- return shimmer.wrapFunction(next, next => function (...args) {
9
+ // Named `next`/arity-1 mirrors the express continuation so wrapCallback skips
10
+ // its name/length rewrite; `_error` only pads the arity and `arguments`
11
+ // forwards whatever next was called with.
12
+ return shimmer.wrapCallback(next, original => function next (_error) {
10
13
  if (bodyParserReadCh.hasSubscribers && req) {
11
14
  const abortController = new AbortController()
12
15
  const body = req.body
@@ -16,7 +19,7 @@ function publishRequestBodyAndNext (req, res, next) {
16
19
  if (abortController.signal.aborted) return
17
20
  }
18
21
 
19
- return next.apply(this, args)
22
+ return original.apply(this, arguments)
20
23
  })
21
24
  }
22
25
 
@@ -90,7 +90,8 @@ function wrapLayerHandle (layer) {
90
90
  }
91
91
 
92
92
  function wrapNext (req, next) {
93
- return shimmer.wrapFunction(next, next => function (error) {
93
+ // Mirror next's name/arity so wrapCallback skips its per-call identity rewrite.
94
+ return shimmer.wrapCallback(next, original => function next (error) {
94
95
  if (error) {
95
96
  errorChannel.publish({ req, error })
96
97
  }
@@ -98,7 +99,7 @@ function wrapNext (req, next) {
98
99
  nextChannel.publish({ req })
99
100
  finishChannel.publish({ req })
100
101
 
101
- next.apply(this, arguments)
102
+ original.apply(this, arguments)
102
103
  })
103
104
  }
104
105
 
@@ -6,7 +6,8 @@ const { channel, addHook } = require('./helpers/instrument')
6
6
  const cookieParserReadCh = channel('datadog:cookie-parser:read:finish')
7
7
 
8
8
  function publishRequestCookieAndNext (req, res, next) {
9
- return shimmer.wrapFunction(next, next => function cookieParserWrapper (...args) {
9
+ // Mirror next's name/arity so wrapCallback skips its per-call identity rewrite.
10
+ return shimmer.wrapCallback(next, original => function next (_error) {
10
11
  if (cookieParserReadCh.hasSubscribers && req) {
11
12
  const abortController = new AbortController()
12
13
 
@@ -17,7 +18,7 @@ function publishRequestCookieAndNext (req, res, next) {
17
18
  if (abortController.signal.aborted) return
18
19
  }
19
20
 
20
- return next.apply(this, args)
21
+ return original.apply(this, arguments)
21
22
  })
22
23
  }
23
24
 
@@ -0,0 +1,19 @@
1
+ 'use strict'
2
+
3
+ const { createRequire } = require('node:module')
4
+ const path = require('node:path')
5
+
6
+ const { patchCucumberWorkerRunTestCase } = require('./cucumber')
7
+
8
+ const appRequire = createRequire(path.join(process.cwd(), 'package.json'))
9
+
10
+ try {
11
+ // Cucumber v13 parallel workers start from an ESM worker.mjs entrypoint, which
12
+ // statically imports the internal runtime Worker before it runs support-code
13
+ // requireModules. The regular module hook does not patch that internal worker
14
+ // import, so this preload is injected into requireModules to patch the cached Worker
15
+ // prototype before Cucumber constructs the worker instance.
16
+ patchCucumberWorkerRunTestCase(appRequire('@cucumber/cucumber/lib/runtime/worker'), true)
17
+ } catch {
18
+ // Ignore preload failures so cucumber can keep running if its internals change.
19
+ }