dd-trace 5.108.0 → 5.109.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 (58) hide show
  1. package/index.d.ts +22 -1
  2. package/package.json +2 -1
  3. package/packages/datadog-instrumentations/src/ai.js +43 -48
  4. package/packages/datadog-instrumentations/src/aws-durable-execution-sdk-js-context-methods.js +18 -0
  5. package/packages/datadog-instrumentations/src/aws-durable-execution-sdk-js.js +111 -0
  6. package/packages/datadog-instrumentations/src/aws-sdk.js +3 -1
  7. package/packages/datadog-instrumentations/src/electron.js +1 -1
  8. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  9. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/aws-durable-execution-sdk-js.js +31 -0
  10. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
  11. package/packages/datadog-instrumentations/src/http/client.js +12 -2
  12. package/packages/datadog-instrumentations/src/ioredis.js +0 -1
  13. package/packages/datadog-instrumentations/src/iovalkey.js +1 -2
  14. package/packages/datadog-instrumentations/src/next.js +34 -0
  15. package/packages/datadog-instrumentations/src/openai.js +77 -18
  16. package/packages/datadog-instrumentations/src/redis.js +0 -1
  17. package/packages/datadog-instrumentations/src/vitest.js +60 -1
  18. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/checkpoint.js +31 -0
  19. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/client.js +55 -0
  20. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/context.js +114 -0
  21. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/handler.js +128 -0
  22. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/index.js +19 -0
  23. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/trace-checkpoint.js +224 -0
  24. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +43 -0
  25. package/packages/datadog-plugin-aws-sdk/src/base.js +1 -7
  26. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +100 -37
  27. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +44 -27
  28. package/packages/datadog-plugin-bullmq/src/filter.js +35 -0
  29. package/packages/datadog-plugin-bullmq/src/producer.js +84 -4
  30. package/packages/datadog-plugin-fs/src/index.js +1 -0
  31. package/packages/datadog-plugin-redis/src/index.js +1 -2
  32. package/packages/datadog-plugin-vitest/src/index.js +4 -1
  33. package/packages/dd-trace/src/aiguard/channels.js +0 -1
  34. package/packages/dd-trace/src/aiguard/index.js +11 -49
  35. package/packages/dd-trace/src/aiguard/integrations/evaluate.js +46 -0
  36. package/packages/dd-trace/src/aiguard/integrations/openai.js +66 -0
  37. package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +78 -0
  38. package/packages/{datadog-instrumentations/src/helpers/ai-messages.js → dd-trace/src/aiguard/messages/openai.js} +85 -193
  39. package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +185 -0
  40. package/packages/dd-trace/src/appsec/channels.js +1 -0
  41. package/packages/dd-trace/src/appsec/downstream_requests.js +111 -58
  42. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/ldap-sensitive-analyzer.js +54 -12
  43. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +5 -1
  44. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +29 -4
  45. package/packages/dd-trace/src/appsec/rasp/ssrf.js +19 -11
  46. package/packages/dd-trace/src/config/generated-config-types.d.ts +3 -0
  47. package/packages/dd-trace/src/config/supported-configurations.json +24 -2
  48. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +2 -0
  49. package/packages/dd-trace/src/dogstatsd.js +15 -8
  50. package/packages/dd-trace/src/exporters/agentless/index.js +7 -5
  51. package/packages/dd-trace/src/exporters/agentless/intake.js +43 -0
  52. package/packages/dd-trace/src/exporters/agentless/writer.js +5 -4
  53. package/packages/dd-trace/src/openfeature/flagging_provider.js +8 -1
  54. package/packages/dd-trace/src/plugins/ci_plugin.js +27 -2
  55. package/packages/dd-trace/src/plugins/index.js +3 -0
  56. package/packages/dd-trace/src/service-naming/schemas/v0/serverless.js +12 -0
  57. package/packages/dd-trace/src/service-naming/schemas/v1/serverless.js +12 -0
  58. package/packages/datadog-instrumentations/src/helpers/openai-ai-guard.js +0 -284
package/index.d.ts CHANGED
@@ -235,6 +235,7 @@ interface Plugins {
235
235
  "anthropic": tracer.plugins.anthropic;
236
236
  "apollo": tracer.plugins.apollo;
237
237
  "avsc": tracer.plugins.avsc;
238
+ "aws-durable-execution-sdk-js": tracer.plugins.aws_durable_execution_sdk_js;
238
239
  "aws-sdk": tracer.plugins.aws_sdk;
239
240
  "azure-cosmos": tracer.plugins.azure_cosmos;
240
241
  "azure-event-hubs": tracer.plugins.azure_event_hubs;
@@ -2351,6 +2352,12 @@ declare namespace tracer {
2351
2352
  */
2352
2353
  interface avsc extends Integration {}
2353
2354
 
2355
+ /**
2356
+ * This plugin automatically instruments the
2357
+ * [aws-durable-execution-sdk-js](https://github.com/aws/aws-durable-execution-sdk-js) module.
2358
+ */
2359
+ interface aws_durable_execution_sdk_js extends Integration {}
2360
+
2354
2361
  /**
2355
2362
  * This plugin automatically instruments the
2356
2363
  * [aws-sdk](https://github.com/aws/aws-sdk-js) module.
@@ -2428,7 +2435,21 @@ declare namespace tracer {
2428
2435
  * This plugin automatically instruments the
2429
2436
  * [bullmq](https://github.com/npmjs/package/bullmq) message queue library.
2430
2437
  */
2431
- interface bullmq extends Instrumentation {}
2438
+ interface bullmq extends Instrumentation {
2439
+ /**
2440
+ * Filter applied to BullMQ producer operations (`Queue.add`, `Queue.addBulk`,
2441
+ * `FlowProducer.add`). Return `false` to skip span creation, trace context
2442
+ * injection, and DSM checkpoint handling for the matching job. Consumer-side
2443
+ * (`Worker`) instrumentation is unaffected.
2444
+ *
2445
+ * @param job.name - The BullMQ job name.
2446
+ * @param job.data - The BullMQ job data.
2447
+ * @param job.opts - The BullMQ job options.
2448
+ * @param job.queueName - The name of the queue the job is being added to.
2449
+ * @returns true to instrument the producer operation, false to skip it.
2450
+ */
2451
+ producerFilter?: (job: { name?: string; data?: unknown; opts?: unknown; queueName?: string }) => boolean;
2452
+ }
2432
2453
 
2433
2454
  interface bunyan extends Integration {}
2434
2455
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dd-trace",
3
- "version": "5.108.0",
3
+ "version": "5.109.0",
4
4
  "description": "Datadog APM tracing client for JavaScript",
5
5
  "main": "index.js",
6
6
  "typings": "index.d.ts",
@@ -102,6 +102,7 @@
102
102
  "test:integration:plugins": "yarn services && mocha \"packages/datadog-plugin-@(${PLUGINS})/test/integration-test/**/${SPEC:-*}*.spec.js\"",
103
103
  "test:integration:plugins:coverage": "yarn services && node ./integration-tests/coverage/run-suite.js \"packages/datadog-plugin-@(${PLUGINS})/test/integration-test/**/${SPEC:-*}*.spec.js\"",
104
104
  "test:unit:plugins": "mocha \"packages/datadog-instrumentations/test/@(${PLUGINS}).spec.js\" \"packages/datadog-plugin-@(${PLUGINS})/test/**/*.spec.js\" --exclude \"packages/datadog-plugin-@(${PLUGINS})/test/integration-test/**/*.spec.js\"",
105
+ "test:release": "mocha \"scripts/release/**/*.spec.js\"",
105
106
  "test:shimmer": "mocha \"packages/datadog-shimmer/test/**/*.spec.js\"",
106
107
  "test:shimmer:ci": "nyc --silent node init && nyc -- npm run test:shimmer",
107
108
  "verify:workflow-job-names": "node scripts/verify-workflow-job-names.js",
@@ -2,29 +2,33 @@
2
2
 
3
3
  const { channel, tracingChannel } = require('dc-polyfill')
4
4
  const shimmer = require('../../datadog-shimmer')
5
- const { convertVercelPromptToMessages, buildOutputMessages } = require('./helpers/ai-messages')
6
5
  const { addHook, getHooks } = require('./helpers/instrument')
7
6
 
8
7
  const vercelAiTracingChannel = tracingChannel('dd-trace:vercel-ai')
9
8
  const vercelAiSpanSetAttributesChannel = channel('dd-trace:vercel-ai:span:setAttributes')
10
- const aiguardChannel = channel('dd-trace:ai:aiguard')
9
+ const doGenerateBeforeChannel = channel('dd-trace:vercel-ai:doGenerate:before')
10
+ const doGenerateAfterChannel = channel('dd-trace:vercel-ai:doGenerate:after')
11
+ const doStreamBeforeChannel = channel('dd-trace:vercel-ai:doStream:before')
12
+ const doStreamAfterChannel = channel('dd-trace:vercel-ai:doStream:after')
11
13
 
12
14
  const tracers = new WeakSet()
13
15
  const wrappedModels = new WeakSet()
14
16
 
15
17
  /**
16
- * Publishes already-converted AI-style messages to the AI Guard evaluation channel.
18
+ * Publishes a provider-native lifecycle payload to a cancelable lifecycle channel.
17
19
  *
18
- * Subscribers push async work into `pending` and abort `abortController` to block.
20
+ * Subscribers push async work into `pending` synchronously during publication and
21
+ * abort `abortController` with an error before the pushed promise resolves to block.
19
22
  *
20
- * @param {Array<object>} messages - AI-style messages to evaluate.
23
+ * @param {object} lifecycleChannel
24
+ * @param {object} payload
21
25
  * @returns {Promise<void>}
22
26
  */
23
- function publishEvaluation (messages) {
27
+ function publishLifecycle (lifecycleChannel, payload) {
24
28
  const abortController = new AbortController()
25
- const ctx = { messages, integration: 'ai', abortController, pending: [] }
29
+ const ctx = { ...payload, abortController, pending: [] }
26
30
 
27
- aiguardChannel.publish(ctx)
31
+ lifecycleChannel.publish(ctx)
28
32
 
29
33
  return Promise.all(ctx.pending).then(() => {
30
34
  if (abortController.signal.aborted) {
@@ -34,12 +38,11 @@ function publishEvaluation (messages) {
34
38
  }
35
39
 
36
40
  /**
37
- * Wraps a Vercel AI language model's doGenerate and doStream methods to evaluate
38
- * messages with AIGuard.
41
+ * Wraps a Vercel AI language model's doGenerate and doStream lifecycle methods.
39
42
  *
40
43
  * @param {object} model - A Vercel AI language model instance
41
44
  */
42
- function wrapModelWithAIGuard (model) {
45
+ function wrapModelWithLifecycle (model) {
43
46
  if (!model || wrappedModels.has(model)) return
44
47
  wrappedModels.add(model)
45
48
 
@@ -48,20 +51,18 @@ function wrapModelWithAIGuard (model) {
48
51
  return function (options) {
49
52
  const originalResult = original.call(this, options)
50
53
 
51
- if (!aiguardChannel.hasSubscribers) return originalResult
54
+ if (!doGenerateBeforeChannel.hasSubscribers && !doGenerateAfterChannel.hasSubscribers) return originalResult
52
55
  if (!options.prompt?.length) return originalResult
53
56
 
54
- const inputMessages = convertVercelPromptToMessages(options.prompt)
55
- if (!inputMessages.length) return originalResult
57
+ const beforeEvaluation = doGenerateBeforeChannel.hasSubscribers
58
+ ? publishLifecycle(doGenerateBeforeChannel, { prompt: options.prompt, options })
59
+ : Promise.resolve()
56
60
 
57
- // Run AI Guard input evaluation and LLM call in parallel.
58
- // The LLM has no side effects so it is safe to discard its result if AI Guard blocks.
59
- return Promise.all([publishEvaluation(inputMessages), originalResult])
61
+ return Promise.all([beforeEvaluation, originalResult])
60
62
  .then(([, result]) => {
61
- if (!result.content?.length) return result
62
- const outputMessages = buildOutputMessages(inputMessages, result.content)
63
- return publishEvaluation(outputMessages)
64
- .then(() => result)
63
+ if (!doGenerateAfterChannel.hasSubscribers || !result.content?.length) return result
64
+ const payload = { prompt: options.prompt, options, result }
65
+ return publishLifecycle(doGenerateAfterChannel, payload).then(() => result)
65
66
  })
66
67
  }
67
68
  })
@@ -72,16 +73,17 @@ function wrapModelWithAIGuard (model) {
72
73
  return function (options) {
73
74
  const originalResult = original.call(this, options)
74
75
 
75
- if (!aiguardChannel.hasSubscribers) return originalResult
76
+ if (!doStreamBeforeChannel.hasSubscribers && !doStreamAfterChannel.hasSubscribers) return originalResult
76
77
  if (!options.prompt?.length) return originalResult
77
78
 
78
- const inputMessages = convertVercelPromptToMessages(options.prompt)
79
- if (!inputMessages.length) return originalResult
79
+ const beforeEvaluation = doStreamBeforeChannel.hasSubscribers
80
+ ? publishLifecycle(doStreamBeforeChannel, { prompt: options.prompt, options })
81
+ : Promise.resolve()
80
82
 
81
- // Run AI Guard input evaluation and LLM call in parallel.
82
- // The LLM has no side effects so it is safe to discard its result if AI Guard blocks.
83
- return Promise.all([publishEvaluation(inputMessages), originalResult])
83
+ return Promise.all([beforeEvaluation, originalResult])
84
84
  .then(([, result]) => {
85
+ if (!doStreamAfterChannel.hasSubscribers) return result
86
+
85
87
  const chunks = []
86
88
  const reader = result.stream.getReader()
87
89
 
@@ -94,26 +96,19 @@ function wrapModelWithAIGuard (model) {
94
96
  }
95
97
 
96
98
  return readAll().then(() => {
97
- const toolCalls = chunks.filter(c => c?.type === 'tool-call')
98
- const text = chunks.filter(c => c?.type === 'text-delta').map(c => c.textDelta).join('')
99
- const content = toolCalls.length ? toolCalls : text ? [{ type: 'text', text }] : []
100
-
101
- const evaluate = content.length
102
- ? publishEvaluation(buildOutputMessages(inputMessages, content))
103
- : Promise.resolve()
104
-
105
- return evaluate.then(() => {
106
- // eslint-disable-next-line n/no-unsupported-features/node-builtins
107
- const stream = new ReadableStream({
108
- start (controller) {
109
- for (const chunk of chunks) {
110
- controller.enqueue(chunk)
111
- }
112
- controller.close()
113
- },
99
+ return publishLifecycle(doStreamAfterChannel, { prompt: options.prompt, options, chunks })
100
+ .then(() => {
101
+ // eslint-disable-next-line n/no-unsupported-features/node-builtins
102
+ const stream = new ReadableStream({
103
+ start (controller) {
104
+ for (const chunk of chunks) {
105
+ controller.enqueue(chunk)
106
+ }
107
+ controller.close()
108
+ },
109
+ })
110
+ return { ...result, stream }
114
111
  })
115
- return { ...result, stream }
116
- })
117
112
  })
118
113
  })
119
114
  }
@@ -230,7 +225,7 @@ for (const hook of getHooks('ai')) {
230
225
  // generateObject, streamObject)
231
226
  tracingChannel('orchestrion:ai:resolveLanguageModel').subscribe({
232
227
  end (ctx) {
233
- wrapModelWithAIGuard(ctx.result)
228
+ wrapModelWithLifecycle(ctx.result)
234
229
  },
235
230
  })
236
231
 
@@ -238,4 +233,4 @@ for (const hook of getHooks('ai')) {
238
233
  })
239
234
  }
240
235
 
241
- module.exports = { wrapModelWithAIGuard }
236
+ module.exports = { wrapModelWithLifecycle }
@@ -0,0 +1,18 @@
1
+ 'use strict'
2
+
3
+ // Methods on `DurableContextImpl` that return a lazy DurablePromise. The rewriter
4
+ // instrumentation generates a `kind: 'Sync'` Orchestrion hook for each so Orchestrion
5
+ // does not eagerly side-chain `.then()`; the runtime instrumentation side-chains the
6
+ // returned DurablePromise itself and publishes a `:settle` channel once it settles,
7
+ // preserving the SDK's lazy semantics. Shared between both so the lists cannot drift.
8
+ module.exports = [
9
+ 'step',
10
+ 'invoke',
11
+ 'runInChildContext',
12
+ 'wait',
13
+ 'waitForCondition',
14
+ 'waitForCallback',
15
+ 'createCallback',
16
+ 'map',
17
+ 'parallel',
18
+ ]
@@ -0,0 +1,111 @@
1
+ 'use strict'
2
+
3
+ const { channel, tracingChannel } = require('dc-polyfill')
4
+ const shimmer = require('../../datadog-shimmer')
5
+ const { addHook, getHooks } = require('./helpers/instrument')
6
+ const LAZY_DURABLE_PROMISE_METHODS = require('./aws-durable-execution-sdk-js-context-methods')
7
+
8
+ for (const method of LAZY_DURABLE_PROMISE_METHODS) {
9
+ const orchestrionCh = tracingChannel(`orchestrion:@aws/durable-execution-sdk-js:DurableContextImpl_${method}`)
10
+ const settleCh = channel(`apm:aws-durable-execution-sdk-js:${method}:settle`)
11
+
12
+ orchestrionCh.end.subscribe(ctx => {
13
+ if (!settleCh.hasSubscribers) return
14
+ observeDurablePromise(ctx.result, error => {
15
+ if (error !== undefined) ctx.error = error
16
+ settleCh.publish(ctx)
17
+ })
18
+ })
19
+ }
20
+
21
+ // runHandler(event, context, executionContext, mode, checkpointToken, handler) creates the
22
+ // DurableContext internally and passes it to the user handler, and drives suspension through
23
+ // executionContext.terminationManager.terminate(). The cross-invocation checkpoint plugin needs
24
+ // both: the DurableContext (to reach the checkpoint manager) and a signal that the execution is
25
+ // suspending. Capture the former off the handler call and surface the latter on a channel.
26
+ const HANDLER_ARG_INDEX = 5
27
+ const EXECUTION_CONTEXT_ARG_INDEX = 2
28
+
29
+ const withDurableExecutionCh = tracingChannel('orchestrion:@aws/durable-execution-sdk-js:withDurableExecution')
30
+ const terminateCh = channel('apm:aws-durable-execution-sdk-js:terminate')
31
+
32
+ withDurableExecutionCh.start.subscribe(ctx => {
33
+ // Only wrap while the plugin is listening (cross-invocation tracing enabled).
34
+ if (!terminateCh.hasSubscribers) return
35
+
36
+ // ctx.arguments is an array-like `arguments` object, not a true Array.
37
+ const args = ctx.arguments
38
+ if (!args) return
39
+
40
+ if (typeof args[HANDLER_ARG_INDEX] === 'function') {
41
+ shimmer.wrap(args, HANDLER_ARG_INDEX, handler => function (event, durableContext) {
42
+ ctx.durableContext = durableContext
43
+ return handler.apply(this, arguments)
44
+ })
45
+ }
46
+
47
+ const terminationManager = args[EXECUTION_CONTEXT_ARG_INDEX]?.terminationManager
48
+ if (typeof terminationManager?.terminate === 'function') {
49
+ shimmer.wrap(terminationManager, 'terminate', terminate => function (options) {
50
+ // Publish before the original runs so the plugin can enqueue the checkpoint while the
51
+ // checkpoint manager is still accepting writes (it flips to "terminating" inside terminate()).
52
+ ctx.terminationReason = options?.reason
53
+ terminateCh.publish(ctx)
54
+ return terminate.apply(this, arguments)
55
+ })
56
+ }
57
+ })
58
+
59
+ // Per-instance settle callback read by the shared prototype wrappers installed by
60
+ // `instrumentDurablePromiseProto`. Stored on the DurablePromise instance so the prototype
61
+ // wrappers stay allocation-free and the promise carries its own observer.
62
+ const ON_SETTLE = Symbol('_dd.durableExecution.onSettle')
63
+
64
+ const instrumentedProtos = new WeakSet()
65
+
66
+ /**
67
+ * Wraps `then`/`catch`/`finally` on a DurablePromise prototype once so every instance shares
68
+ * the same wrappers instead of being re-wrapped individually. The wrappers side-chain a settle
69
+ * observer the first time user code chains off the promise, then clear `ON_SETTLE` so the
70
+ * observer attaches at most once (a promise settles only once). Instances we don't observe
71
+ * never set `ON_SETTLE`, so for them the wrappers are a single property read and a passthrough.
72
+ * @param {object} proto - The DurablePromise prototype (`Object.getPrototypeOf(dp)`).
73
+ * @returns {void}
74
+ */
75
+ function instrumentDurablePromiseProto (proto) {
76
+ if (instrumentedProtos.has(proto)) return
77
+ instrumentedProtos.add(proto)
78
+
79
+ // Capture the genuine `.then` before wrapping so the settle observer side-chains without
80
+ // recursing back into the wrapper below.
81
+ const originalThen = proto.then
82
+
83
+ shimmer.massWrap(proto, ['then', 'catch', 'finally'], original => function (...args) {
84
+ const onSettle = this[ON_SETTLE]
85
+ if (onSettle !== undefined) {
86
+ this[ON_SETTLE] = undefined
87
+ originalThen.call(this, () => onSettle(), err => onSettle(err))
88
+ }
89
+ return original.apply(this, args)
90
+ })
91
+ }
92
+
93
+ /**
94
+ * Registers a settle observer on a returned DurablePromise. Callers pair `kind: 'Sync'` with this
95
+ * helper so `onSettle` only fires after user code first awaits / chains, preserving the SDK's
96
+ * lazy semantics.
97
+ * @param {object} dp - The returned DurablePromise instance.
98
+ * @param {(err: unknown) => void} onSettle - Called once with `undefined` on success or the
99
+ * rejection reason on failure.
100
+ * @returns {void}
101
+ */
102
+ function observeDurablePromise (dp, onSettle) {
103
+ if (!dp || typeof dp.then !== 'function') return
104
+ instrumentDurablePromiseProto(Object.getPrototypeOf(dp))
105
+ dp[ON_SETTLE] = onSettle
106
+ }
107
+
108
+ for (const hook of getHooks('@aws/durable-execution-sdk-js')) {
109
+ hook.file = null
110
+ addHook(hook, exports => exports)
111
+ }
@@ -150,6 +150,7 @@ function wrapDeserialize (deserialize, headersCh, responseIndex = 0) {
150
150
  function wrapSmithySend (send) {
151
151
  return function (command, ...args) {
152
152
  const cb = args.at(-1)
153
+ const cbExists = typeof cb === 'function'
153
154
  const serviceIdentifier = this.config.serviceId.toLowerCase()
154
155
  const channelSuffix = getChannelSuffix(serviceIdentifier)
155
156
  const channels = getChannelBag(channelSuffix)
@@ -188,6 +189,7 @@ function wrapSmithySend (send) {
188
189
  operation,
189
190
  awsService: clientName,
190
191
  request,
192
+ cbExists,
191
193
  }
192
194
 
193
195
  return channels.start.runStores(ctx, () => {
@@ -197,7 +199,7 @@ function wrapSmithySend (send) {
197
199
  channels.region.publish(ctx)
198
200
  })
199
201
 
200
- if (typeof cb === 'function') {
202
+ if (cbExists) {
201
203
  args[args.length - 1] = shimmer.wrapCallback(cb, cb => function (err, result) {
202
204
  addResponse(ctx, err, result)
203
205
 
@@ -218,7 +218,7 @@ addHook({ name: 'electron', versions: ['>=37.0.0'] }, electron => {
218
218
  wrap(ipcRenderer, 'removeAllListeners', createWrapRemoveAllListeners(listeners))
219
219
 
220
220
  ipcRenderer.send('datadog:apm:renderer:patched')
221
- } else {
221
+ } else if (ipcMain) {
222
222
  wrap(ipcMain, 'addListener', createWrapAddListener(mainReceiveCh, listeners))
223
223
  wrap(ipcMain, 'handle', createWrapAddListener(mainHandleCh, handlers))
224
224
  wrap(ipcMain, 'handleOnce', createWrapAddListener(mainHandleCh, handlers))
@@ -22,6 +22,7 @@ module.exports = {
22
22
  '@modelcontextprotocol/sdk': () => require('../modelcontextprotocol-sdk'),
23
23
  'apollo-server-core': () => require('../apollo-server-core'),
24
24
  '@aws-sdk/smithy-client': () => require('../aws-sdk'),
25
+ '@aws/durable-execution-sdk-js': () => require('../aws-durable-execution-sdk-js'),
25
26
  '@azure/cosmos': { esmFirst: true, fn: () => require('../azure-cosmos') },
26
27
  '@azure/event-hubs': () => require('../azure-event-hubs'),
27
28
  '@azure/functions': () => require('../azure-functions'),
@@ -0,0 +1,31 @@
1
+ 'use strict'
2
+
3
+ const contextMethods = require('../../../aws-durable-execution-sdk-js-context-methods')
4
+
5
+ const baseModule = { name: '@aws/durable-execution-sdk-js', versionRange: '>=1.1.0' }
6
+
7
+ const buildEntries = filePath => {
8
+ const module = { ...baseModule, filePath }
9
+ return [
10
+ {
11
+ module,
12
+ functionQuery: { functionName: 'runHandler', kind: 'Async' },
13
+ channelName: 'withDurableExecution',
14
+ },
15
+ ...contextMethods.map(methodName => ({
16
+ module,
17
+ functionQuery: { className: 'DurableContextImpl', methodName, kind: 'Sync' },
18
+ channelName: `DurableContextImpl_${methodName}`,
19
+ })),
20
+ {
21
+ module,
22
+ functionQuery: { className: 'CheckpointManager', methodName: 'checkpoint', kind: 'Async' },
23
+ channelName: 'CheckpointManager_checkpoint',
24
+ },
25
+ ]
26
+ }
27
+
28
+ module.exports = [
29
+ ...buildEntries('dist/index.mjs'),
30
+ ...buildEntries('dist-cjs/index.js'),
31
+ ]
@@ -8,4 +8,5 @@ module.exports = [
8
8
  ...require('./langgraph'),
9
9
  ...require('./modelcontextprotocol-sdk'),
10
10
  ...require('./playwright'),
11
+ ...require('./aws-durable-execution-sdk-js'),
11
12
  ]
@@ -14,6 +14,7 @@ const finishChannel = channel('apm:http:client:request:finish')
14
14
  const endChannel = channel('apm:http:client:request:end')
15
15
  const asyncStartChannel = channel('apm:http:client:request:asyncStart')
16
16
  const errorChannel = channel('apm:http:client:request:error')
17
+ const responseStartChannel = channel('apm:http:client:response:start')
17
18
  const responseFinishChannel = channel('apm:http:client:response:finish')
18
19
 
19
20
  addHook({ name: 'http' }, hookFn)
@@ -78,10 +79,15 @@ function setupResponseInstrumentation (ctx, res) {
78
79
  let dataReadStarted = false
79
80
 
80
81
  const { shouldCollectBody } = ctx
81
- const bodyChunks = shouldCollectBody ? [] : null
82
+
83
+ let bodyChunks = null
84
+
85
+ if (shouldCollectBody) {
86
+ bodyChunks = []
87
+ }
82
88
 
83
89
  const collectChunk = chunk => {
84
- if (!shouldCollectBody || !chunk) return
90
+ if (!bodyChunks || !chunk) return
85
91
 
86
92
  if (typeof chunk === 'string') {
87
93
  bodyChunks.push(chunk)
@@ -229,6 +235,10 @@ function patch (http, methodName) {
229
235
  res.once('end', finish)
230
236
  res.once(errorMonitor, finish)
231
237
 
238
+ if (responseStartChannel.hasSubscribers) {
239
+ responseStartChannel.publish({ ctx, res })
240
+ }
241
+
232
242
  const instrumentation = setupResponseInstrumentation(ctx, res)
233
243
 
234
244
  if (!instrumentation) {
@@ -26,7 +26,6 @@ function wrapRedis (Redis) {
26
26
  }
27
27
 
28
28
  const ctx = {
29
- db: options.db,
30
29
  command: command.name,
31
30
  args: command.args,
32
31
  connectionOptions,
@@ -18,10 +18,9 @@ addHook({ name: 'iovalkey', versions: ['>=0.0.1'] }, Valkey => {
18
18
 
19
19
  const options = this.options || {}
20
20
  const connectionName = options.connectionName
21
- const db = options.db
22
21
  const connectionOptions = { host: options.host, port: options.port }
23
22
 
24
- const ctx = { db, command: command.name, args: command.args, connectionOptions, connectionName }
23
+ const ctx = { command: command.name, args: command.args, connectionOptions, connectionName }
25
24
  return startCh.runStores(ctx, () => {
26
25
  command.promise.then(() => finish(finishCh, errorCh, ctx), err => finish(finishCh, errorCh, ctx, err))
27
26
 
@@ -220,6 +220,40 @@ addHook({
220
220
  return NextRequestAdapter
221
221
  })
222
222
 
223
+ // From next 15.4.1 each app-route build inlines its own copy of `fromNodeNextRequest`, so the hook
224
+ // above no longer maps the node request to the app-route NextRequest and a thrown handler error
225
+ // cannot reach `finish`. The app-route runtime reports real errors (redirect/notFound and other
226
+ // control-flow signals excluded by next) through `RouteModule.onRequestError`, which next loads from
227
+ // a precompiled `app-route*.runtime.{dev,prod}.js` bundle regardless of how the route chunk is
228
+ // bundled. The bundler/experimental part of the name is matched with a pattern rather than
229
+ // enumerated, so a variant next adds later is picked up without a code change.
230
+ const patchedAppRouteModules = new WeakSet()
231
+
232
+ function wrapOnRequestError (onRequestError) {
233
+ return function (req, error) {
234
+ if (error) {
235
+ errorChannel.publish({ error })
236
+ }
237
+ return onRequestError.apply(this, arguments)
238
+ }
239
+ }
240
+
241
+ function instrumentAppRouteRuntime (runtime) {
242
+ const AppRouteRouteModule = runtime.AppRouteRouteModule
243
+ const proto = AppRouteRouteModule?.prototype
244
+ if (proto && typeof proto.onRequestError === 'function' && !patchedAppRouteModules.has(AppRouteRouteModule)) {
245
+ patchedAppRouteModules.add(AppRouteRouteModule)
246
+ shimmer.wrap(proto, 'onRequestError', wrapOnRequestError)
247
+ }
248
+ return runtime
249
+ }
250
+
251
+ addHook({
252
+ name: 'next',
253
+ versions: ['>=15.4.1'],
254
+ filePattern: String.raw`dist/compiled/next-server/app-route[\w-]*\.runtime\.(?:dev|prod)\.js$`,
255
+ }, instrumentAppRouteRuntime)
256
+
223
257
  addHook({
224
258
  name: 'next',
225
259
  versions: ['>=11.1'],