dd-trace 6.14.0 → 6.16.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/README.md +15 -0
- package/ci/test-optimization-validation/framework-adapters/cucumber.js +4 -2
- package/ci/test-optimization-validation/package-check.js +4 -1
- package/ci/test-optimization-validation/scenarios/ci-wiring.js +1 -1
- package/index.d.ts +12 -0
- package/next.d.ts +27 -0
- package/next.js +3 -0
- package/package.json +17 -13
- package/packages/datadog-esbuild/index.js +14 -1
- package/packages/datadog-esbuild/src/resolver.js +284 -0
- package/packages/datadog-esbuild/src/utils.js +163 -119
- package/packages/datadog-instrumentations/src/ai.js +39 -97
- package/packages/datadog-instrumentations/src/anthropic.js +76 -134
- package/packages/datadog-instrumentations/src/aws-durable-execution-sdk-js.js +1 -1
- package/packages/datadog-instrumentations/src/azure-cosmos.js +1 -1
- package/packages/datadog-instrumentations/src/azure-durable-functions.js +23 -9
- package/packages/datadog-instrumentations/src/azure-functions.js +48 -0
- package/packages/datadog-instrumentations/src/bullmq.js +1 -1
- package/packages/datadog-instrumentations/src/claude-agent-sdk.js +1 -1
- package/packages/datadog-instrumentations/src/dd-trace-api.js +2 -2
- package/packages/datadog-instrumentations/src/graphql.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/bundler-register.js +39 -14
- package/packages/datadog-instrumentations/src/helpers/instrument.js +25 -8
- package/packages/datadog-instrumentations/src/helpers/instrumentation-utils.js +72 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +9 -64
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +46 -14
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/azure-durable-functions.js +17 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +1 -5
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +6 -7
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +99 -70
- package/packages/datadog-instrumentations/src/jest.js +141 -25
- package/packages/datadog-instrumentations/src/langchain.js +1 -1
- package/packages/datadog-instrumentations/src/langgraph.js +1 -1
- package/packages/datadog-instrumentations/src/mercurius.js +1 -1
- package/packages/datadog-instrumentations/src/mocha/main.js +41 -5
- package/packages/datadog-instrumentations/src/modelcontextprotocol-sdk.js +1 -1
- package/packages/datadog-instrumentations/src/openai-agents.js +1 -1
- package/packages/datadog-instrumentations/src/openai.js +54 -76
- package/packages/datadog-instrumentations/src/playwright.js +4 -0
- package/packages/datadog-instrumentations/src/vitest-worker.js +7 -10
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +7 -1
- package/packages/datadog-plugin-azure-cosmos/src/index.js +1 -1
- package/packages/datadog-plugin-azure-durable-functions/src/index.js +96 -1
- package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -24
- package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +2 -36
- package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-request-context.js +31 -0
- package/packages/datadog-plugin-graphql/src/resolve-error.js +33 -11
- package/packages/datadog-turbopack/index.js +212 -0
- package/packages/datadog-turbopack/src/constants.js +5 -0
- package/packages/datadog-turbopack/src/loader.js +500 -0
- package/packages/dd-trace/src/aiguard/index.js +2 -1
- package/packages/dd-trace/src/aiguard/integrations/anthropic.js +53 -25
- package/packages/dd-trace/src/aiguard/integrations/evaluate.js +49 -25
- package/packages/dd-trace/src/aiguard/integrations/index.js +3 -2
- package/packages/dd-trace/src/aiguard/integrations/openai.js +59 -30
- package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +116 -42
- package/packages/dd-trace/src/aiguard/messages/utils.js +29 -1
- package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +25 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/command-sensitive-analyzer.js +3 -1
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +531 -70
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +2 -2
- package/packages/dd-trace/src/appsec/telemetry/api_security.js +1 -3
- package/packages/dd-trace/src/appsec/telemetry/common.js +4 -0
- package/packages/dd-trace/src/appsec/telemetry/index.js +23 -5
- package/packages/dd-trace/src/appsec/telemetry/rasp.js +1 -20
- package/packages/dd-trace/src/appsec/telemetry/user.js +1 -3
- package/packages/dd-trace/src/appsec/telemetry/waf.js +1 -20
- package/packages/dd-trace/src/ci-visibility/requests/request.js +11 -0
- package/packages/dd-trace/src/ci-visibility/requests/video-request.js +4 -0
- package/packages/dd-trace/src/config/generated-config-types.d.ts +2 -0
- package/packages/dd-trace/src/config/helper.js +10 -2
- package/packages/dd-trace/src/config/index.js +20 -5
- package/packages/dd-trace/src/config/supported-configurations.json +10 -0
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +14 -7
- package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +14 -1
- package/packages/dd-trace/src/debugger/devtools_client/request-options.js +1 -6
- package/packages/dd-trace/src/evp_proxy/direct.js +2 -28
- package/packages/dd-trace/src/evp_proxy/discovery.js +3 -4
- package/packages/dd-trace/src/evp_proxy/path.js +12 -3
- package/packages/dd-trace/src/exporters/agentless/writer.js +3 -1
- package/packages/dd-trace/src/exporters/common/docker.js +4 -1
- package/packages/dd-trace/src/exporters/common/proxy.js +55 -0
- package/packages/dd-trace/src/exporters/common/request.js +11 -3
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/meter_provider.js +5 -4
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +106 -25
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +12 -1
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +8 -2
- package/packages/dd-trace/src/plugin_manager.js +9 -3
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/util/test.js +4 -1
- package/packages/dd-trace/src/priority_sampler.js +4 -0
- package/packages/dd-trace/src/sampling_rule.js +3 -1
- package/packages/dd-trace/src/span_processor.js +18 -3
- package/packages/dd-trace/src/telemetry/send-data.js +5 -4
- package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/utils.js +0 -10
|
@@ -718,7 +718,7 @@ function wrapVitestTestRunner (VitestTestRunner) {
|
|
|
718
718
|
const isAtf = attemptToFixTasks.has(task)
|
|
719
719
|
const isEfd = efdRetryTasks.has(task)
|
|
720
720
|
const shouldTrackStatuses = isEfd || isAtf
|
|
721
|
-
const
|
|
721
|
+
const shouldResetStatus = isEfd || isAtf
|
|
722
722
|
const statuses = isAtf ? attemptToFixTaskToStatuses.get(task) : taskToStatuses.get(task)
|
|
723
723
|
|
|
724
724
|
// These clauses handle task.repeats, whether EFD is enabled or not
|
|
@@ -738,13 +738,10 @@ function wrapVitestTestRunner (VitestTestRunner) {
|
|
|
738
738
|
if (shouldTrackStatuses && statuses) {
|
|
739
739
|
statuses.push(lastExecutionStatus)
|
|
740
740
|
}
|
|
741
|
-
if (
|
|
742
|
-
//
|
|
743
|
-
//
|
|
744
|
-
|
|
745
|
-
// if the last attempt passes, vitest would consider the test as failed
|
|
746
|
-
// but after this change, it will consider the test as passed
|
|
747
|
-
task.result.state = 'pass'
|
|
741
|
+
if (shouldResetStatus) {
|
|
742
|
+
// Reset to Vitest's neutral state so a previous failure does not affect the next repetition.
|
|
743
|
+
// A terminal pass state can leak into reporters before the next repetition determines its status.
|
|
744
|
+
task.result.state = 'run'
|
|
748
745
|
}
|
|
749
746
|
}
|
|
750
747
|
} else if (numRepetition === task.repeats) {
|
|
@@ -759,8 +756,8 @@ function wrapVitestTestRunner (VitestTestRunner) {
|
|
|
759
756
|
} else {
|
|
760
757
|
testPassCh.publish({ task, ...ctx.currentStore })
|
|
761
758
|
}
|
|
762
|
-
if (
|
|
763
|
-
task.result.state = '
|
|
759
|
+
if (shouldResetStatus) {
|
|
760
|
+
task.result.state = 'run'
|
|
764
761
|
}
|
|
765
762
|
}
|
|
766
763
|
|
|
@@ -500,6 +500,8 @@ function extractMessagesFromConverseContent (role, contentBlocks) {
|
|
|
500
500
|
if (block == null || typeof block !== 'object') continue
|
|
501
501
|
if (typeof block.text === 'string') {
|
|
502
502
|
content += block.text
|
|
503
|
+
} else if (typeof block.guardContent?.text?.text === 'string') {
|
|
504
|
+
content += block.guardContent.text.text
|
|
503
505
|
} else if (block.toolUse) {
|
|
504
506
|
toolCalls.push(buildToolCall(block.toolUse))
|
|
505
507
|
} else if (block.toolResult) {
|
|
@@ -611,7 +613,11 @@ function extractRequestParamsConverse (params) {
|
|
|
611
613
|
const prompt = []
|
|
612
614
|
if (params.system) {
|
|
613
615
|
for (const block of params.system) {
|
|
614
|
-
if (typeof block?.text === 'string')
|
|
616
|
+
if (typeof block?.text === 'string') {
|
|
617
|
+
prompt.push({ content: block.text, role: 'system' })
|
|
618
|
+
} else if (typeof block?.guardContent?.text?.text === 'string') {
|
|
619
|
+
prompt.push({ content: block.guardContent.text.text, role: 'system' })
|
|
620
|
+
}
|
|
615
621
|
}
|
|
616
622
|
}
|
|
617
623
|
if (params.messages) {
|
|
@@ -26,7 +26,7 @@ class AzureCosmosPlugin extends DatabasePlugin {
|
|
|
26
26
|
const result = ctx.result
|
|
27
27
|
if (result?.code) span.setTag('db.response.status_code', (result.code).toString())
|
|
28
28
|
if (result?.substatus) span.setTag('cosmosdb.response.sub_status_code', result.substatus)
|
|
29
|
-
|
|
29
|
+
this.finish(ctx)
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const TracingPlugin = require('../../dd-trace/src/plugins/tracing')
|
|
4
|
+
const { AUTO_KEEP } = require('../../../ext/priority')
|
|
5
|
+
const { writeTraceparent, writeTracestate } = require('../../dd-trace/src/carrier')
|
|
6
|
+
|
|
7
|
+
const ORCHESTRATION_FAILURE_END_CHANNEL =
|
|
8
|
+
'tracing:orchestrion:durable-functions:TaskOrchestrationExecutor_failure:end'
|
|
9
|
+
const ORCHESTRATOR_COMPLETED_EVENT_TYPE = 13
|
|
4
10
|
|
|
5
11
|
class AzureDurableFunctionsPlugin extends TracingPlugin {
|
|
6
12
|
static get id () { return 'azure-durable-functions' }
|
|
@@ -9,9 +15,35 @@ class AzureDurableFunctionsPlugin extends TracingPlugin {
|
|
|
9
15
|
static get type () { return 'serverless' }
|
|
10
16
|
static get kind () { return 'server' }
|
|
11
17
|
|
|
18
|
+
addTraceSubs () {
|
|
19
|
+
super.addTraceSubs()
|
|
20
|
+
this.addSub(ORCHESTRATION_FAILURE_END_CHANNEL, this.orchestrationFailure)
|
|
21
|
+
}
|
|
22
|
+
|
|
12
23
|
bindStart (ctx) {
|
|
24
|
+
// Continue the trace propagated by the Durable Functions host (W3C traceparent
|
|
25
|
+
// supplied on the invocation's traceContext) so activity/entity invocations join
|
|
26
|
+
// the same trace as the HTTP root instead of each starting a new root.
|
|
27
|
+
let childOf
|
|
28
|
+
if (ctx.traceparent) {
|
|
29
|
+
// extract() returns null when the carrier can't be parsed. Normalize to
|
|
30
|
+
// undefined so startSpan still falls back to any active in-process parent
|
|
31
|
+
// rather than being forced to start a brand new root span.
|
|
32
|
+
const carrier = {}
|
|
33
|
+
|
|
34
|
+
writeTraceparent(carrier, ctx.traceparent)
|
|
35
|
+
if (ctx.tracestate) {
|
|
36
|
+
writeTracestate(carrier, ctx.tracestate)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
childOf = this.tracer.extract('text_map', carrier) ?? undefined
|
|
40
|
+
}
|
|
41
|
+
const extractedAsDrop = childOf?._sampling.priority < AUTO_KEEP
|
|
42
|
+
|
|
13
43
|
const span = this.startSpan(this.operationName(), {
|
|
14
|
-
|
|
44
|
+
startTime: ctx.startTime,
|
|
45
|
+
childOf,
|
|
46
|
+
kind: ctx.trigger === 'Orchestration' ? 'server' : 'internal',
|
|
15
47
|
type: 'serverless',
|
|
16
48
|
|
|
17
49
|
meta: {
|
|
@@ -29,6 +61,19 @@ class AzureDurableFunctionsPlugin extends TracingPlugin {
|
|
|
29
61
|
)
|
|
30
62
|
}
|
|
31
63
|
|
|
64
|
+
// The host clears the W3C sampled flag in traceparent while datadog tracestate
|
|
65
|
+
// still says keep, so extraction would drop this chunk. Re-apply only the propagated
|
|
66
|
+
// `s` priority when it indicates keep, preserving the extracted sampling mechanism.
|
|
67
|
+
if (span._prioritySampler && childOf && extractedAsDrop) {
|
|
68
|
+
const propagatedPriority = propagatedSamplingPriority(childOf._tracestate)
|
|
69
|
+
if (propagatedPriority >= AUTO_KEEP) {
|
|
70
|
+
const spanContext = span.context()
|
|
71
|
+
if (spanContext._parentId === childOf._spanId) {
|
|
72
|
+
spanContext._sampling.priority = propagatedPriority
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
32
77
|
ctx.span = span
|
|
33
78
|
return ctx.currentStore
|
|
34
79
|
}
|
|
@@ -44,6 +89,56 @@ class AzureDurableFunctionsPlugin extends TracingPlugin {
|
|
|
44
89
|
asyncStart (ctx) {
|
|
45
90
|
super.finish(ctx)
|
|
46
91
|
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Tags the active initial orchestration span or creates a failure-only span after
|
|
95
|
+
* a resumed orchestration throws. The executor publishes synchronously before
|
|
96
|
+
* Durable Functions serializes the error, so this does not add a promise to
|
|
97
|
+
* orchestration activations.
|
|
98
|
+
*
|
|
99
|
+
* @param {{ arguments?: unknown[], error?: unknown }} executorCtx
|
|
100
|
+
* @returns {void}
|
|
101
|
+
*/
|
|
102
|
+
orchestrationFailure (executorCtx) {
|
|
103
|
+
const args = executorCtx?.arguments
|
|
104
|
+
if (!Array.isArray(args)) return
|
|
105
|
+
|
|
106
|
+
const invocationContext = args[0]
|
|
107
|
+
const history = args[1]
|
|
108
|
+
if (!Array.isArray(history)) return
|
|
109
|
+
|
|
110
|
+
const hasPreviousActivation = history.some(
|
|
111
|
+
event => event?.EventType === ORCHESTRATOR_COMPLETED_EVENT_TYPE
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
if (!hasPreviousActivation) {
|
|
115
|
+
this.addError(executorCtx.error)
|
|
116
|
+
return
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const traceContext = invocationContext?.traceContext
|
|
120
|
+
const ctx = {
|
|
121
|
+
trigger: 'Orchestration',
|
|
122
|
+
functionName: invocationContext?.functionName,
|
|
123
|
+
traceparent: traceContext?.traceParent,
|
|
124
|
+
tracestate: traceContext?.traceState,
|
|
125
|
+
error: executorCtx.error,
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
this.bindStart(ctx)
|
|
129
|
+
this.error(ctx)
|
|
130
|
+
super.finish(ctx)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Read the datadog-propagated sampling priority (`dd=...;s:<n>`) from a W3C
|
|
135
|
+
// tracestate. Returns undefined when there is no datadog tracestate or no valid
|
|
136
|
+
// `s` value, so callers can distinguish "no propagated decision" from a drop.
|
|
137
|
+
function propagatedSamplingPriority (tracestate) {
|
|
138
|
+
return tracestate?.forVendor('dd', state => {
|
|
139
|
+
const priority = Number(state.get('s'))
|
|
140
|
+
return Number.isInteger(priority) ? priority : undefined
|
|
141
|
+
})
|
|
47
142
|
}
|
|
48
143
|
|
|
49
144
|
module.exports = AzureDurableFunctionsPlugin
|
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
const { getMessageSize } = require('../../dd-trace/src/datastreams')
|
|
4
4
|
const ConsumerPlugin = require('../../dd-trace/src/plugins/consumer')
|
|
5
|
-
const SpanContext = require('../../dd-trace/src/opentracing/span_context')
|
|
6
|
-
const id = require('../../dd-trace/src/id')
|
|
7
5
|
const { storage } = require('../../datadog-core')
|
|
6
|
+
const reconstructPubSubRequestContext = require('./pubsub-request-context')
|
|
8
7
|
|
|
9
8
|
/**
|
|
10
9
|
* PULL SUBSCRIPTION: Service explicitly pulls messages from Pub/Sub and processes them.
|
|
@@ -75,27 +74,6 @@ class GoogleCloudPubsubConsumerPlugin extends ConsumerPlugin {
|
|
|
75
74
|
})
|
|
76
75
|
}
|
|
77
76
|
|
|
78
|
-
#reconstructPubSubRequestContext (attrs) {
|
|
79
|
-
const traceIdLower = attrs['_dd.pubsub_request.trace_id']
|
|
80
|
-
const spanId = attrs['_dd.pubsub_request.span_id']
|
|
81
|
-
const traceIdUpper = attrs['_dd.pubsub_request.p.tid']
|
|
82
|
-
|
|
83
|
-
if (!traceIdLower || !spanId) return null
|
|
84
|
-
|
|
85
|
-
const traceId128 = traceIdUpper ? traceIdUpper + traceIdLower : traceIdLower.padStart(32, '0')
|
|
86
|
-
const traceId = id(traceId128, 16)
|
|
87
|
-
const parentId = id(spanId, 16)
|
|
88
|
-
|
|
89
|
-
const tags = {}
|
|
90
|
-
if (traceIdUpper) tags['_dd.p.tid'] = traceIdUpper
|
|
91
|
-
|
|
92
|
-
return new SpanContext({
|
|
93
|
-
traceId,
|
|
94
|
-
spanId: parentId,
|
|
95
|
-
tags,
|
|
96
|
-
})
|
|
97
|
-
}
|
|
98
|
-
|
|
99
77
|
start (ctx) {
|
|
100
78
|
if (!this.config.dsmEnabled) return
|
|
101
79
|
const { message } = ctx
|
|
@@ -128,7 +106,7 @@ class GoogleCloudPubsubConsumerPlugin extends ConsumerPlugin {
|
|
|
128
106
|
|
|
129
107
|
const isFirstMessage = batchIndex === 0
|
|
130
108
|
if (isFirstMessage && batchRequestSpanId) {
|
|
131
|
-
const pubsubRequestContext =
|
|
109
|
+
const pubsubRequestContext = reconstructPubSubRequestContext(message.attributes)
|
|
132
110
|
if (pubsubRequestContext) {
|
|
133
111
|
childOf = pubsubRequestContext
|
|
134
112
|
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const TracingPlugin = require('../../dd-trace/src/plugins/tracing')
|
|
4
|
-
const SpanContext = require('../../dd-trace/src/opentracing/span_context')
|
|
5
|
-
const id = require('../../dd-trace/src/id')
|
|
6
4
|
const log = require('../../dd-trace/src/log')
|
|
5
|
+
const reconstructPubSubRequestContext = require('./pubsub-request-context')
|
|
7
6
|
|
|
8
7
|
// WeakMap to track push receive spans by request
|
|
9
8
|
const pushReceiveSpans = new WeakMap()
|
|
@@ -69,7 +68,7 @@ class GoogleCloudPubsubPushSubscriptionPlugin extends TracingPlugin {
|
|
|
69
68
|
}
|
|
70
69
|
|
|
71
70
|
const originalContext = this.#extractContext(messageData)
|
|
72
|
-
const pubsubRequestContext =
|
|
71
|
+
const pubsubRequestContext = reconstructPubSubRequestContext(messageData.attrs) || originalContext
|
|
73
72
|
const isSameTrace = pubsubRequestContext &&
|
|
74
73
|
originalContext?.toTraceId() === pubsubRequestContext.toTraceId()
|
|
75
74
|
|
|
@@ -109,39 +108,6 @@ class GoogleCloudPubsubPushSubscriptionPlugin extends TracingPlugin {
|
|
|
109
108
|
return this.tracer.extract('text_map', messageData.attrs)
|
|
110
109
|
}
|
|
111
110
|
|
|
112
|
-
#reconstructPubSubContext (attrs) {
|
|
113
|
-
/**
|
|
114
|
-
* Reconstruct the batch publish span context from message attributes.
|
|
115
|
-
*
|
|
116
|
-
* When a batch is published, the producer injects:
|
|
117
|
-
* - _dd.pubsub_request.trace_id: lower 64 bits of the batch span's trace ID (hex)
|
|
118
|
-
* - _dd.pubsub_request.span_id: the batch span's span ID (hex)
|
|
119
|
-
* - _dd.pubsub_request.p.tid: upper 64 bits of trace ID (hex, optional for 128-bit traces)
|
|
120
|
-
*
|
|
121
|
-
* This context represents the "pubsub.request" span on the producer side.
|
|
122
|
-
* We use it to create span links, connecting each pubsub.push.receive span back to the original batch.
|
|
123
|
-
*/
|
|
124
|
-
const traceIdLower = attrs['_dd.pubsub_request.trace_id']
|
|
125
|
-
const spanId = attrs['_dd.pubsub_request.span_id']
|
|
126
|
-
const traceIdUpper = attrs['_dd.pubsub_request.p.tid']
|
|
127
|
-
|
|
128
|
-
if (!traceIdLower || !spanId) return null
|
|
129
|
-
|
|
130
|
-
// Reconstruct full 128-bit trace ID (or pad 64-bit to 128-bit)
|
|
131
|
-
const traceId128 = traceIdUpper ? traceIdUpper + traceIdLower : traceIdLower.padStart(32, '0')
|
|
132
|
-
const traceId = id(traceId128, 16)
|
|
133
|
-
const parentId = id(spanId, 16)
|
|
134
|
-
|
|
135
|
-
const tags = {}
|
|
136
|
-
if (traceIdUpper) tags['_dd.p.tid'] = traceIdUpper
|
|
137
|
-
|
|
138
|
-
return new SpanContext({
|
|
139
|
-
traceId,
|
|
140
|
-
spanId: parentId,
|
|
141
|
-
tags,
|
|
142
|
-
})
|
|
143
|
-
}
|
|
144
|
-
|
|
145
111
|
#createPushReceiveSpan (messageData, parentContext, linkContext) {
|
|
146
112
|
const { message, subscription, topicName, attrs } = messageData
|
|
147
113
|
const subscriptionName = subscription?.slice(subscription.lastIndexOf('/') + 1) ?? subscription
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const SpanContext = require('../../dd-trace/src/opentracing/span_context')
|
|
4
|
+
const id = require('../../dd-trace/src/id')
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @param {Record<string, string>} attributes
|
|
8
|
+
* @returns {import('../../dd-trace/src/opentracing/span_context') | null}
|
|
9
|
+
*/
|
|
10
|
+
function reconstructPubSubRequestContext (attributes) {
|
|
11
|
+
const traceIdLower = attributes['_dd.pubsub_request.trace_id']
|
|
12
|
+
const spanId = attributes['_dd.pubsub_request.span_id']
|
|
13
|
+
const traceIdUpper = attributes['_dd.pubsub_request.p.tid']
|
|
14
|
+
|
|
15
|
+
if (!traceIdLower || !spanId) return null
|
|
16
|
+
|
|
17
|
+
const traceId128 = traceIdUpper ? traceIdUpper + traceIdLower : traceIdLower.padStart(32, '0')
|
|
18
|
+
const traceId = id(traceId128, 16)
|
|
19
|
+
const parentId = id(spanId, 16)
|
|
20
|
+
|
|
21
|
+
const tags = {}
|
|
22
|
+
if (traceIdUpper) tags['_dd.p.tid'] = traceIdUpper
|
|
23
|
+
|
|
24
|
+
return new SpanContext({
|
|
25
|
+
traceId,
|
|
26
|
+
spanId: parentId,
|
|
27
|
+
tags,
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = reconstructPubSubRequestContext
|
|
@@ -61,28 +61,50 @@ class GraphQLToolsResolveErrorPlugin extends TracingPlugin {
|
|
|
61
61
|
* @param {readonly object[]} [fieldNodes]
|
|
62
62
|
*/
|
|
63
63
|
function recordResolveErrorForPath (error, path, fieldNodes) {
|
|
64
|
-
const fieldNode = fieldNodes?.[0]
|
|
65
64
|
const rootCtx = legacyStorage.getStore()?.graphqlRootCtx
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
}
|
|
65
|
+
if (!rootCtx?.config.collapse || !rootCtx.fields) return
|
|
66
|
+
|
|
67
|
+
const field = findResolveField(rootCtx.fields, getPathString(path), fieldNodes?.[0])
|
|
68
|
+
if (field) recordResolveError(field, error)
|
|
72
69
|
}
|
|
73
70
|
|
|
74
71
|
/**
|
|
75
|
-
* @param {string} fieldPath
|
|
76
72
|
* @param {(string | number)[]} path
|
|
77
|
-
* @returns {
|
|
73
|
+
* @returns {string}
|
|
78
74
|
*/
|
|
79
|
-
function
|
|
75
|
+
function getPathString (path) {
|
|
80
76
|
let pathString = ''
|
|
81
77
|
for (let index = 0; index < path.length; index++) {
|
|
82
78
|
if (index !== 0) pathString += '.'
|
|
83
79
|
pathString += typeof path[index] === 'number' ? '*' : path[index]
|
|
84
80
|
}
|
|
85
|
-
return pathString
|
|
81
|
+
return pathString
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* @param {Map<unknown, object>} fields
|
|
86
|
+
* @param {string} pathString
|
|
87
|
+
* @param {object} [fieldNode]
|
|
88
|
+
* @returns {object | undefined}
|
|
89
|
+
*/
|
|
90
|
+
function findResolveField (fields, pathString, fieldNode) {
|
|
91
|
+
const field = fields.get(pathString)
|
|
92
|
+
if (field === undefined) return
|
|
93
|
+
|
|
94
|
+
const parentTypeFields = field.parentTypeFields
|
|
95
|
+
if (parentTypeFields === undefined) {
|
|
96
|
+
return fieldNode === undefined || field.fieldNode === fieldNode ? field : undefined
|
|
97
|
+
}
|
|
98
|
+
if (parentTypeFields.parentTypeName !== undefined) {
|
|
99
|
+
if (fieldNode === undefined || parentTypeFields.fieldNode === fieldNode) return parentTypeFields
|
|
100
|
+
return field.fieldNode === fieldNode ? field : undefined
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
let matchingField
|
|
104
|
+
for (const candidate of parentTypeFields.values()) {
|
|
105
|
+
if (fieldNode === undefined || candidate.fieldNode === fieldNode) matchingField = candidate
|
|
106
|
+
}
|
|
107
|
+
return matchingField
|
|
86
108
|
}
|
|
87
109
|
|
|
88
110
|
/**
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs')
|
|
4
|
+
const Module = require('node:module')
|
|
5
|
+
const path = require('node:path')
|
|
6
|
+
|
|
7
|
+
const satisfies = require('../../vendor/dist/semifies')
|
|
8
|
+
const hooks = require('../datadog-instrumentations/src/helpers/hooks')
|
|
9
|
+
const { SYNTHETIC_EXTENSION } = require('./src/constants')
|
|
10
|
+
|
|
11
|
+
const BUILTIN_MODULES = new Set(Module.builtinModules)
|
|
12
|
+
const loader = path.join(__dirname, 'src/loader.js')
|
|
13
|
+
const EXTENSIONLESS_PATH_PATTERN = /(?:^|\/)[^/.]+$/
|
|
14
|
+
const PHASE_PRODUCTION_SERVER = 'phase-production-server'
|
|
15
|
+
const SOURCE_EXTENSIONS = ['*.js', '*.cjs', '*.mjs', '*.jsx', '*.ts', '*.cts', '*.mts', '*.tsx']
|
|
16
|
+
const SOURCE_PATH_PATTERN = /\.(?:cjs|cts|js|jsx|mjs|mts|ts|tsx)$/
|
|
17
|
+
const PACKAGE_PATH_PATTERN = createPackagePathPattern()
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Adds Datadog instrumentation to a Next.js Turbopack configuration.
|
|
21
|
+
*
|
|
22
|
+
* @param {object|Promise<object>|Function} [nextConfig]
|
|
23
|
+
* @returns {Function}
|
|
24
|
+
*/
|
|
25
|
+
function withDatadogTurbopack (nextConfig = {}) {
|
|
26
|
+
const nextMajor = getNextMajor()
|
|
27
|
+
|
|
28
|
+
return function datadogNextConfig (...args) {
|
|
29
|
+
const config = typeof nextConfig === 'function'
|
|
30
|
+
? nextConfig.apply(this, args)
|
|
31
|
+
: nextConfig
|
|
32
|
+
if (config && typeof config.then === 'function') {
|
|
33
|
+
return config.then(config => configureTurbopack(config, args[0], nextMajor))
|
|
34
|
+
}
|
|
35
|
+
return configureTurbopack(config, args[0], nextMajor)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @param {object|undefined} config
|
|
41
|
+
* @param {unknown} phase
|
|
42
|
+
* @param {number} nextMajor
|
|
43
|
+
* @returns {object}
|
|
44
|
+
*/
|
|
45
|
+
function configureTurbopack (config, phase, nextMajor) {
|
|
46
|
+
const normalized = normalizeConfig(config)
|
|
47
|
+
if (phase === PHASE_PRODUCTION_SERVER || hasDatadogLoader(normalized.turbopack?.rules)) return normalized
|
|
48
|
+
|
|
49
|
+
const turbopack = normalized.turbopack ?? {}
|
|
50
|
+
const configured = nextMajor === 15 ? addLegacyRule(turbopack) : addModernRules(turbopack)
|
|
51
|
+
return { ...normalized, turbopack: configured }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @param {object|undefined} config
|
|
56
|
+
* @returns {object}
|
|
57
|
+
*/
|
|
58
|
+
function normalizeConfig (config) {
|
|
59
|
+
if (config === undefined) return {}
|
|
60
|
+
if (!isObject(config)) {
|
|
61
|
+
throw new TypeError('withDatadogTurbopack expects a Next.js configuration object, promise, or function')
|
|
62
|
+
}
|
|
63
|
+
if (config.turbopack !== undefined && !isObject(config.turbopack)) {
|
|
64
|
+
throw new TypeError('nextConfig.turbopack must be an object')
|
|
65
|
+
}
|
|
66
|
+
if (config.turbopack?.rules !== undefined && !isObject(config.turbopack.rules)) {
|
|
67
|
+
throw new TypeError('nextConfig.turbopack.rules must be an object')
|
|
68
|
+
}
|
|
69
|
+
if (config.turbopack?.conditions !== undefined && !isObject(config.turbopack.conditions)) {
|
|
70
|
+
throw new TypeError('nextConfig.turbopack.conditions must be an object')
|
|
71
|
+
}
|
|
72
|
+
return config
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @param {unknown} value
|
|
77
|
+
* @returns {boolean}
|
|
78
|
+
*/
|
|
79
|
+
function isObject (value) {
|
|
80
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* @param {object} turbopack
|
|
85
|
+
* @returns {object}
|
|
86
|
+
*/
|
|
87
|
+
function addModernRules (turbopack) {
|
|
88
|
+
const rules = { ...turbopack.rules }
|
|
89
|
+
for (const extension of SOURCE_EXTENSIONS) {
|
|
90
|
+
appendRule(rules, extension, {
|
|
91
|
+
condition: { all: ['node', 'foreign', { path: PACKAGE_PATH_PATTERN }] },
|
|
92
|
+
loaders: [createLoader()],
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
appendRule(rules, '*', {
|
|
96
|
+
as: `*${SYNTHETIC_EXTENSION}`,
|
|
97
|
+
condition: {
|
|
98
|
+
all: ['node', 'foreign', { path: PACKAGE_PATH_PATTERN }, { path: EXTENSIONLESS_PATH_PATTERN }],
|
|
99
|
+
},
|
|
100
|
+
loaders: [createLoader()],
|
|
101
|
+
})
|
|
102
|
+
return { ...turbopack, rules }
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* @param {Record<string, object|object[]>} rules
|
|
107
|
+
* @param {string} extension
|
|
108
|
+
* @param {object} rule
|
|
109
|
+
*/
|
|
110
|
+
function appendRule (rules, extension, rule) {
|
|
111
|
+
const existing = rules[extension]
|
|
112
|
+
if (existing === undefined) {
|
|
113
|
+
rules[extension] = rule
|
|
114
|
+
} else {
|
|
115
|
+
rules[extension] = Array.isArray(existing) ? [...existing, rule] : [existing, rule]
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* @param {object} turbopack
|
|
121
|
+
* @returns {object}
|
|
122
|
+
*/
|
|
123
|
+
function addLegacyRule (turbopack) {
|
|
124
|
+
const name = '#dd-trace/modules'
|
|
125
|
+
const conditions = { ...turbopack.conditions }
|
|
126
|
+
const rules = { ...turbopack.rules }
|
|
127
|
+
if (Object.hasOwn(conditions, name) || Object.hasOwn(rules, name)) {
|
|
128
|
+
throw new Error(`Next.js Turbopack configuration already uses the reserved condition ${name}`)
|
|
129
|
+
}
|
|
130
|
+
conditions[name] = {
|
|
131
|
+
all: [
|
|
132
|
+
{ path: PACKAGE_PATH_PATTERN },
|
|
133
|
+
{ any: [{ path: SOURCE_PATH_PATTERN }, { path: EXTENSIONLESS_PATH_PATTERN }] },
|
|
134
|
+
],
|
|
135
|
+
}
|
|
136
|
+
rules[name] = { node: { foreign: { loaders: [createLoader()] } } }
|
|
137
|
+
return { ...turbopack, conditions, rules }
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* @returns {{ loader: string, options: object }}
|
|
142
|
+
*/
|
|
143
|
+
function createLoader () {
|
|
144
|
+
return { loader, options: {} }
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* @param {unknown} value
|
|
149
|
+
* @returns {boolean}
|
|
150
|
+
*/
|
|
151
|
+
function hasDatadogLoader (value) {
|
|
152
|
+
if (Array.isArray(value)) return value.some(hasDatadogLoader)
|
|
153
|
+
if (!value || typeof value !== 'object') return false
|
|
154
|
+
if (/** @type {{ loader?: unknown }} */ (value).loader === loader) return true
|
|
155
|
+
return Object.values(value).some(hasDatadogLoader)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* @returns {RegExp}
|
|
160
|
+
*/
|
|
161
|
+
function createPackagePathPattern () {
|
|
162
|
+
const names = []
|
|
163
|
+
for (const name of Object.keys(hooks)) {
|
|
164
|
+
if (!name.startsWith('.') && !BUILTIN_MODULES.has(name)) names.push(escapeRegExp(name))
|
|
165
|
+
}
|
|
166
|
+
names.sort()
|
|
167
|
+
return new RegExp(`(?:^|/)(?:${names.join('|')})(?:/|$)`)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* @param {string} value
|
|
172
|
+
* @returns {string}
|
|
173
|
+
*/
|
|
174
|
+
function escapeRegExp (value) {
|
|
175
|
+
return value.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* @returns {number}
|
|
180
|
+
*/
|
|
181
|
+
function getNextMajor () {
|
|
182
|
+
const entrypoints = []
|
|
183
|
+
if (require.main?.filename) entrypoints.push(require.main.filename)
|
|
184
|
+
entrypoints.push(path.join(process.cwd(), 'package.json'))
|
|
185
|
+
|
|
186
|
+
let version
|
|
187
|
+
let resolutionError
|
|
188
|
+
for (const entrypoint of entrypoints) {
|
|
189
|
+
try {
|
|
190
|
+
const appRequire = Module.createRequire(entrypoint)
|
|
191
|
+
version = JSON.parse(fs.readFileSync(appRequire.resolve('next/package.json'), 'utf8')).version
|
|
192
|
+
break
|
|
193
|
+
} catch (error) {
|
|
194
|
+
resolutionError = error
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (!version) {
|
|
198
|
+
throw new Error('withDatadogTurbopack could not resolve the active Next.js installation', {
|
|
199
|
+
cause: resolutionError,
|
|
200
|
+
})
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (!satisfies(version, '>=0')) {
|
|
204
|
+
throw new Error(`withDatadogTurbopack could not parse Next.js version ${version}`)
|
|
205
|
+
}
|
|
206
|
+
if (!satisfies(version, '>=15.5.0')) {
|
|
207
|
+
throw new RangeError(`withDatadogTurbopack requires Next.js 15.5 or newer; found ${version}`)
|
|
208
|
+
}
|
|
209
|
+
return Number.parseInt(version, 10)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
module.exports = { withDatadogTurbopack }
|