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.
- package/index.d.ts +22 -1
- package/package.json +2 -1
- package/packages/datadog-instrumentations/src/ai.js +43 -48
- package/packages/datadog-instrumentations/src/aws-durable-execution-sdk-js-context-methods.js +18 -0
- package/packages/datadog-instrumentations/src/aws-durable-execution-sdk-js.js +111 -0
- package/packages/datadog-instrumentations/src/aws-sdk.js +3 -1
- package/packages/datadog-instrumentations/src/electron.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/aws-durable-execution-sdk-js.js +31 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
- package/packages/datadog-instrumentations/src/http/client.js +12 -2
- package/packages/datadog-instrumentations/src/ioredis.js +0 -1
- package/packages/datadog-instrumentations/src/iovalkey.js +1 -2
- package/packages/datadog-instrumentations/src/next.js +34 -0
- package/packages/datadog-instrumentations/src/openai.js +77 -18
- package/packages/datadog-instrumentations/src/redis.js +0 -1
- package/packages/datadog-instrumentations/src/vitest.js +60 -1
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/checkpoint.js +31 -0
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/client.js +55 -0
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/context.js +114 -0
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/handler.js +128 -0
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/index.js +19 -0
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/trace-checkpoint.js +224 -0
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +43 -0
- package/packages/datadog-plugin-aws-sdk/src/base.js +1 -7
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +100 -37
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +44 -27
- package/packages/datadog-plugin-bullmq/src/filter.js +35 -0
- package/packages/datadog-plugin-bullmq/src/producer.js +84 -4
- package/packages/datadog-plugin-fs/src/index.js +1 -0
- package/packages/datadog-plugin-redis/src/index.js +1 -2
- package/packages/datadog-plugin-vitest/src/index.js +4 -1
- package/packages/dd-trace/src/aiguard/channels.js +0 -1
- package/packages/dd-trace/src/aiguard/index.js +11 -49
- package/packages/dd-trace/src/aiguard/integrations/evaluate.js +46 -0
- package/packages/dd-trace/src/aiguard/integrations/openai.js +66 -0
- package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +78 -0
- package/packages/{datadog-instrumentations/src/helpers/ai-messages.js → dd-trace/src/aiguard/messages/openai.js} +85 -193
- package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +185 -0
- package/packages/dd-trace/src/appsec/channels.js +1 -0
- package/packages/dd-trace/src/appsec/downstream_requests.js +111 -58
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/ldap-sensitive-analyzer.js +54 -12
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +5 -1
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +29 -4
- package/packages/dd-trace/src/appsec/rasp/ssrf.js +19 -11
- package/packages/dd-trace/src/config/generated-config-types.d.ts +3 -0
- package/packages/dd-trace/src/config/supported-configurations.json +24 -2
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +2 -0
- package/packages/dd-trace/src/dogstatsd.js +15 -8
- package/packages/dd-trace/src/exporters/agentless/index.js +7 -5
- package/packages/dd-trace/src/exporters/agentless/intake.js +43 -0
- package/packages/dd-trace/src/exporters/agentless/writer.js +5 -4
- package/packages/dd-trace/src/openfeature/flagging_provider.js +8 -1
- package/packages/dd-trace/src/plugins/ci_plugin.js +27 -2
- package/packages/dd-trace/src/plugins/index.js +3 -0
- package/packages/dd-trace/src/service-naming/schemas/v0/serverless.js +12 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/serverless.js +12 -0
- 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.
|
|
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
|
|
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
|
|
18
|
+
* Publishes a provider-native lifecycle payload to a cancelable lifecycle channel.
|
|
17
19
|
*
|
|
18
|
-
* Subscribers push async work into `pending`
|
|
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 {
|
|
23
|
+
* @param {object} lifecycleChannel
|
|
24
|
+
* @param {object} payload
|
|
21
25
|
* @returns {Promise<void>}
|
|
22
26
|
*/
|
|
23
|
-
function
|
|
27
|
+
function publishLifecycle (lifecycleChannel, payload) {
|
|
24
28
|
const abortController = new AbortController()
|
|
25
|
-
const ctx = {
|
|
29
|
+
const ctx = { ...payload, abortController, pending: [] }
|
|
26
30
|
|
|
27
|
-
|
|
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
|
|
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
|
|
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 (!
|
|
54
|
+
if (!doGenerateBeforeChannel.hasSubscribers && !doGenerateAfterChannel.hasSubscribers) return originalResult
|
|
52
55
|
if (!options.prompt?.length) return originalResult
|
|
53
56
|
|
|
54
|
-
const
|
|
55
|
-
|
|
57
|
+
const beforeEvaluation = doGenerateBeforeChannel.hasSubscribers
|
|
58
|
+
? publishLifecycle(doGenerateBeforeChannel, { prompt: options.prompt, options })
|
|
59
|
+
: Promise.resolve()
|
|
56
60
|
|
|
57
|
-
|
|
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
|
|
63
|
-
return
|
|
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 (!
|
|
76
|
+
if (!doStreamBeforeChannel.hasSubscribers && !doStreamAfterChannel.hasSubscribers) return originalResult
|
|
76
77
|
if (!options.prompt?.length) return originalResult
|
|
77
78
|
|
|
78
|
-
const
|
|
79
|
-
|
|
79
|
+
const beforeEvaluation = doStreamBeforeChannel.hasSubscribers
|
|
80
|
+
? publishLifecycle(doStreamBeforeChannel, { prompt: options.prompt, options })
|
|
81
|
+
: Promise.resolve()
|
|
80
82
|
|
|
81
|
-
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
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 = {
|
|
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 (
|
|
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
|
+
]
|
|
@@ -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
|
-
|
|
82
|
+
|
|
83
|
+
let bodyChunks = null
|
|
84
|
+
|
|
85
|
+
if (shouldCollectBody) {
|
|
86
|
+
bodyChunks = []
|
|
87
|
+
}
|
|
82
88
|
|
|
83
89
|
const collectChunk = chunk => {
|
|
84
|
-
if (!
|
|
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) {
|
|
@@ -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 = {
|
|
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'],
|