dd-trace 5.96.0 → 5.98.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 +60 -2
- package/package.json +9 -7
- package/packages/datadog-esbuild/index.js +20 -9
- package/packages/datadog-instrumentations/src/child_process.js +7 -17
- package/packages/datadog-instrumentations/src/crypto.js +1 -2
- package/packages/datadog-instrumentations/src/cucumber.js +69 -4
- package/packages/datadog-instrumentations/src/cypress-config.js +318 -0
- package/packages/datadog-instrumentations/src/cypress.js +86 -4
- package/packages/datadog-instrumentations/src/dns.js +1 -2
- package/packages/datadog-instrumentations/src/express.js +4 -4
- package/packages/datadog-instrumentations/src/fs.js +27 -29
- package/packages/datadog-instrumentations/src/graphql.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/bundler-register.js +41 -13
- package/packages/datadog-instrumentations/src/helpers/hook.js +31 -6
- package/packages/datadog-instrumentations/src/helpers/hooks.js +12 -19
- package/packages/datadog-instrumentations/src/helpers/instrument.js +27 -13
- package/packages/datadog-instrumentations/src/helpers/register.js +103 -142
- package/packages/datadog-instrumentations/src/http/client.js +2 -3
- package/packages/datadog-instrumentations/src/http/server.js +2 -5
- package/packages/datadog-instrumentations/src/http2/client.js +1 -3
- package/packages/datadog-instrumentations/src/http2/server.js +1 -3
- package/packages/datadog-instrumentations/src/jest.js +117 -16
- package/packages/datadog-instrumentations/src/limitd-client.js +1 -1
- package/packages/datadog-instrumentations/src/mocha/utils.js +12 -1
- package/packages/datadog-instrumentations/src/net.js +2 -8
- package/packages/datadog-instrumentations/src/pino.js +1 -1
- package/packages/datadog-instrumentations/src/playwright.js +4 -1
- package/packages/datadog-instrumentations/src/prisma.js +1 -2
- package/packages/datadog-instrumentations/src/redis.js +12 -6
- package/packages/datadog-instrumentations/src/selenium.js +4 -1
- package/packages/datadog-instrumentations/src/sequelize.js +1 -1
- package/packages/datadog-instrumentations/src/url.js +1 -3
- package/packages/datadog-instrumentations/src/vitest.js +5 -1
- package/packages/datadog-instrumentations/src/vm.js +1 -3
- package/packages/datadog-plugin-aws-sdk/src/base.js +5 -4
- package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/redshift.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/sns.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -0
- package/packages/datadog-plugin-cucumber/src/index.js +13 -3
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +166 -6
- package/packages/datadog-plugin-cypress/src/index.js +59 -2
- package/packages/datadog-plugin-fs/src/index.js +1 -1
- package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -1
- package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +2 -7
- package/packages/datadog-plugin-graphql/src/resolve.js +1 -1
- package/packages/datadog-plugin-http/src/client.js +1 -1
- package/packages/datadog-plugin-http/src/server.js +10 -2
- package/packages/datadog-plugin-http2/src/client.js +1 -1
- package/packages/datadog-plugin-http2/src/server.js +10 -2
- package/packages/datadog-plugin-jest/src/index.js +4 -2
- package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +31 -4
- package/packages/datadog-plugin-mocha/src/index.js +5 -2
- package/packages/datadog-plugin-mongodb-core/src/index.js +3 -3
- package/packages/datadog-plugin-mysql/src/index.js +1 -1
- package/packages/datadog-plugin-next/src/index.js +10 -16
- package/packages/datadog-plugin-openai/src/services.js +1 -0
- package/packages/datadog-plugin-pg/src/index.js +1 -1
- package/packages/datadog-plugin-tedious/src/index.js +1 -1
- package/packages/datadog-plugin-ws/src/close.js +1 -1
- package/packages/datadog-plugin-ws/src/receiver.js +1 -1
- package/packages/datadog-webpack/index.js +3 -3
- package/packages/dd-trace/index.js +12 -10
- package/packages/dd-trace/src/agent/url.js +2 -2
- package/packages/dd-trace/src/aiguard/sdk.js +26 -22
- package/packages/dd-trace/src/appsec/blocked_templates.js +4 -3
- package/packages/dd-trace/src/appsec/blocking.js +64 -33
- package/packages/dd-trace/src/appsec/iast/iast-plugin.js +1 -1
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +1 -1
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +1 -1
- package/packages/dd-trace/src/appsec/remote_config.js +1 -0
- package/packages/dd-trace/src/appsec/sdk/index.js +4 -0
- package/packages/dd-trace/src/appsec/sdk/set_user.js +1 -1
- package/packages/dd-trace/src/appsec/sdk/track_event.js +5 -5
- package/packages/dd-trace/src/appsec/sdk/user_blocking.js +2 -2
- package/packages/dd-trace/src/appsec/sdk/utils.js +4 -2
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +6 -1
- package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +4 -0
- package/packages/dd-trace/src/config/defaults.js +315 -146
- package/packages/dd-trace/src/config/generated-config-types.d.ts +9 -1
- package/packages/dd-trace/src/config/helper.js +59 -10
- package/packages/dd-trace/src/config/index.js +587 -1496
- package/packages/dd-trace/src/config/parsers.js +256 -0
- package/packages/dd-trace/src/config/remote_config.js +59 -2
- package/packages/dd-trace/src/config/supported-configurations.json +406 -432
- package/packages/dd-trace/src/constants.js +1 -0
- package/packages/dd-trace/src/crashtracking/crashtracker.js +7 -1
- package/packages/dd-trace/src/crashtracking/index.js +1 -7
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +5 -2
- package/packages/dd-trace/src/debugger/index.js +1 -1
- package/packages/dd-trace/src/dogstatsd.js +12 -9
- package/packages/dd-trace/src/encode/0.4.js +8 -7
- package/packages/dd-trace/src/encode/span-stats.js +4 -1
- package/packages/dd-trace/src/exporters/agent/writer.js +7 -1
- package/packages/dd-trace/src/exporters/common/request.js +9 -0
- package/packages/dd-trace/src/exporters/common/writer.js +12 -2
- package/packages/dd-trace/src/heap_snapshots.js +3 -0
- package/packages/dd-trace/src/index.js +5 -2
- package/packages/dd-trace/src/lambda/runtime/ritm.js +6 -6
- package/packages/dd-trace/src/llmobs/index.js +4 -1
- package/packages/dd-trace/src/llmobs/plugins/ai/index.js +5 -1
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +60 -12
- package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +4 -2
- package/packages/dd-trace/src/llmobs/sdk.js +12 -8
- package/packages/dd-trace/src/llmobs/span_processor.js +1 -1
- package/packages/dd-trace/src/llmobs/tagger.js +9 -6
- package/packages/dd-trace/src/llmobs/writers/base.js +2 -0
- package/packages/dd-trace/src/llmobs/writers/util.js +3 -0
- package/packages/dd-trace/src/log/index.js +20 -59
- package/packages/dd-trace/src/log/writer.js +7 -19
- package/packages/dd-trace/src/noop/proxy.js +8 -0
- package/packages/dd-trace/src/openfeature/remote_config.js +6 -1
- package/packages/dd-trace/src/opentelemetry/context_manager.js +6 -4
- package/packages/dd-trace/src/opentelemetry/logs/index.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +1 -1
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +17 -2
- package/packages/dd-trace/src/opentelemetry/otlp/protobuf_loader.js +14 -2
- package/packages/dd-trace/src/opentelemetry/otlp/trace.proto +358 -0
- package/packages/dd-trace/src/opentelemetry/otlp/trace_service.proto +78 -0
- package/packages/dd-trace/src/opentelemetry/trace/index.js +75 -0
- package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +66 -0
- package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +332 -0
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +9 -4
- package/packages/dd-trace/src/opentracing/tracer.js +9 -4
- package/packages/dd-trace/src/payload-tagging/config/index.js +6 -5
- package/packages/dd-trace/src/plugin_manager.js +8 -6
- package/packages/dd-trace/src/plugins/ci_plugin.js +4 -0
- package/packages/dd-trace/src/plugins/log_plugin.js +3 -0
- package/packages/dd-trace/src/plugins/plugin.js +11 -13
- package/packages/dd-trace/src/plugins/storage.js +2 -2
- package/packages/dd-trace/src/plugins/tracing.js +22 -5
- package/packages/dd-trace/src/plugins/util/test.js +2 -0
- package/packages/dd-trace/src/plugins/util/web.js +6 -88
- package/packages/dd-trace/src/process-tags/index.js +3 -0
- package/packages/dd-trace/src/profiler.js +27 -2
- package/packages/dd-trace/src/profiling/config.js +73 -241
- package/packages/dd-trace/src/profiling/exporter_cli.js +1 -4
- package/packages/dd-trace/src/profiling/exporters/event_serializer.js +6 -2
- package/packages/dd-trace/src/profiling/profiler.js +78 -109
- package/packages/dd-trace/src/profiling/profilers/events.js +2 -3
- package/packages/dd-trace/src/profiling/profilers/wall.js +89 -6
- package/packages/dd-trace/src/profiling/ssi-heuristics.js +4 -1
- package/packages/dd-trace/src/propagation-hash/index.js +2 -1
- package/packages/dd-trace/src/proxy.js +40 -6
- package/packages/dd-trace/src/remote_config/index.js +3 -0
- package/packages/dd-trace/src/require-package-json.js +8 -4
- package/packages/dd-trace/src/ritm.js +58 -26
- package/packages/dd-trace/src/runtime_metrics/index.js +3 -0
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +18 -11
- package/packages/dd-trace/src/sampler.js +1 -1
- package/packages/dd-trace/src/service-naming/index.js +1 -1
- package/packages/dd-trace/src/service-naming/schemas/definition.js +4 -1
- package/packages/dd-trace/src/service-naming/schemas/util.js +15 -1
- package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +24 -1
- package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +60 -0
- package/packages/dd-trace/src/service-naming/schemas/v0/web.js +17 -1
- package/packages/dd-trace/src/service-naming/schemas/v0/websocket.js +5 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +17 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/web.js +11 -1
- package/packages/dd-trace/src/service-naming/schemas/v1/websocket.js +6 -0
- package/packages/dd-trace/src/span_stats.js +5 -1
- package/packages/dd-trace/src/standalone/index.js +3 -0
- package/packages/dd-trace/src/telemetry/index.js +2 -3
- package/packages/dd-trace/src/telemetry/send-data.js +5 -19
- package/packages/dd-trace/src/telemetry/session-propagation.js +19 -44
- package/packages/dd-trace/src/telemetry/telemetry.js +28 -171
- package/packages/dd-trace/src/tracer.js +2 -2
- package/packages/dd-trace/src/util.js +0 -9
- package/vendor/dist/@apm-js-collab/code-transformer/index.js +28 -6
- package/vendor/dist/protobufjs/index.js +1 -1
- package/packages/dd-trace/src/log/utils.js +0 -16
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const fs = require('fs')
|
|
4
|
+
const os = require('os')
|
|
5
|
+
const path = require('path')
|
|
6
|
+
const { pathToFileURL } = require('url')
|
|
7
|
+
const { channel } = require('./helpers/instrument')
|
|
8
|
+
|
|
9
|
+
const DD_CONFIG_WRAPPED = Symbol('dd-trace.cypress.config.wrapped')
|
|
10
|
+
|
|
11
|
+
const setupNodeEventsCh = channel('ci:cypress:setup-node-events')
|
|
12
|
+
|
|
13
|
+
// Ensure the cypress plugin is loaded so it can subscribe to our channel.
|
|
14
|
+
// Normally, plugins are loaded when their npm module is required (via addHook),
|
|
15
|
+
// but plain-object configs don't require('cypress'), so the plugin would never
|
|
16
|
+
// be instantiated in the Cypress Config Manager child process.
|
|
17
|
+
const loadCh = channel('dd-trace:instrumentation:load')
|
|
18
|
+
if (loadCh.hasSubscribers) {
|
|
19
|
+
loadCh.publish({ name: 'cypress' })
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const noopTask = {
|
|
23
|
+
'dd:testSuiteStart': () => null,
|
|
24
|
+
'dd:beforeEach': () => ({}),
|
|
25
|
+
'dd:afterEach': () => null,
|
|
26
|
+
'dd:addTags': () => null,
|
|
27
|
+
'dd:log': () => null,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @param {unknown} value
|
|
32
|
+
* @returns {boolean}
|
|
33
|
+
*/
|
|
34
|
+
function isPlainObject (value) {
|
|
35
|
+
if (!value || typeof value !== 'object') return false
|
|
36
|
+
const prototype = Object.getPrototypeOf(value)
|
|
37
|
+
return prototype === Object.prototype || prototype === null
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Cypress allows setupNodeEvents to return partial config fragments that it
|
|
42
|
+
* diffs and merges into the resolved config. Preserve that behavior here so
|
|
43
|
+
* the wrapper does not drop user-provided config updates.
|
|
44
|
+
*
|
|
45
|
+
* @param {object} config Cypress resolved config object
|
|
46
|
+
* @param {unknown} updatedConfig value returned from setupNodeEvents
|
|
47
|
+
* @returns {object} resolved config with returned overrides applied
|
|
48
|
+
*/
|
|
49
|
+
function mergeReturnedConfig (config, updatedConfig) {
|
|
50
|
+
if (!isPlainObject(updatedConfig) || updatedConfig === config) {
|
|
51
|
+
return config
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const mergedConfig = { ...config }
|
|
55
|
+
|
|
56
|
+
for (const [key, value] of Object.entries(updatedConfig)) {
|
|
57
|
+
mergedConfig[key] = isPlainObject(value) && isPlainObject(mergedConfig[key])
|
|
58
|
+
? mergeReturnedConfig(mergedConfig[key], value)
|
|
59
|
+
: value
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return mergedConfig
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Creates a temporary wrapper support file under os.tmpdir() that loads
|
|
67
|
+
* dd-trace's browser-side hooks before the user's original support file.
|
|
68
|
+
* Returns the wrapper path (for cleanup) or undefined if injection was skipped.
|
|
69
|
+
*
|
|
70
|
+
* @param {object} config Cypress resolved config object
|
|
71
|
+
* @returns {string|undefined} wrapper file path, or undefined if skipped
|
|
72
|
+
*/
|
|
73
|
+
function injectSupportFile (config) {
|
|
74
|
+
const originalSupportFile = config.supportFile
|
|
75
|
+
if (!originalSupportFile || originalSupportFile === false) return
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
const content = fs.readFileSync(originalSupportFile, 'utf8')
|
|
79
|
+
// Naive check: skip lines starting with // or * to avoid matching commented-out imports.
|
|
80
|
+
const hasActiveDdTraceImport = content.split('\n').some(line => {
|
|
81
|
+
const trimmed = line.trim()
|
|
82
|
+
return trimmed.includes('dd-trace/ci/cypress/support') &&
|
|
83
|
+
!trimmed.startsWith('//') && !trimmed.startsWith('*')
|
|
84
|
+
})
|
|
85
|
+
if (hasActiveDdTraceImport) return
|
|
86
|
+
} catch {
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const ddSupportFile = require.resolve('../../../ci/cypress/support')
|
|
91
|
+
const wrapperFile = path.join(os.tmpdir(), `dd-cypress-support-${process.pid}.mjs`)
|
|
92
|
+
|
|
93
|
+
// Always use ESM: it can import both CJS and ESM support files.
|
|
94
|
+
const wrapperContent =
|
|
95
|
+
`import ${JSON.stringify(ddSupportFile)}\nimport ${JSON.stringify(originalSupportFile)}\n`
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
fs.writeFileSync(wrapperFile, wrapperContent)
|
|
99
|
+
config.supportFile = wrapperFile
|
|
100
|
+
return wrapperFile
|
|
101
|
+
} catch {
|
|
102
|
+
// Can't write wrapper - skip injection
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Registers dd-trace's Cypress hooks (before:run, after:spec, after:run, tasks)
|
|
108
|
+
* and injects the support file. Communicates with the plugin layer via
|
|
109
|
+
* the `ci:cypress:setup-node-events` diagnostic channel, avoiding direct
|
|
110
|
+
* tracer references in the instrumentation layer.
|
|
111
|
+
*
|
|
112
|
+
* @param {Function} on Cypress event registration function
|
|
113
|
+
* @param {object} config Cypress resolved config object
|
|
114
|
+
* @param {Function[]} userAfterSpecHandlers user's after:spec handlers collected from wrappedOn
|
|
115
|
+
* @param {Function[]} userAfterRunHandlers user's after:run handlers collected from wrappedOn
|
|
116
|
+
* @returns {object} the config object (possibly modified)
|
|
117
|
+
*/
|
|
118
|
+
function registerDdTraceHooks (on, config, userAfterSpecHandlers, userAfterRunHandlers) {
|
|
119
|
+
const wrapperFile = injectSupportFile(config)
|
|
120
|
+
|
|
121
|
+
const cleanupWrapper = () => {
|
|
122
|
+
if (wrapperFile) {
|
|
123
|
+
try { fs.unlinkSync(wrapperFile) } catch { /* best effort */ }
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const registerAfterRunWithCleanup = () => {
|
|
128
|
+
on('after:run', (results) => {
|
|
129
|
+
const chain = userAfterRunHandlers.reduce(
|
|
130
|
+
(p, h) => p.then(() => h(results)),
|
|
131
|
+
Promise.resolve()
|
|
132
|
+
)
|
|
133
|
+
return chain.finally(cleanupWrapper)
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const registerNoopHandlers = () => {
|
|
138
|
+
for (const h of userAfterSpecHandlers) on('after:spec', h)
|
|
139
|
+
registerAfterRunWithCleanup()
|
|
140
|
+
on('task', noopTask)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (!setupNodeEventsCh.hasSubscribers) {
|
|
144
|
+
registerNoopHandlers()
|
|
145
|
+
return config
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Publish to the plugin layer via diagnostic channel.
|
|
149
|
+
// The subscriber sets `payload.registered = true` and optionally
|
|
150
|
+
// `payload.configPromise` when it handles the event.
|
|
151
|
+
const payload = {
|
|
152
|
+
on,
|
|
153
|
+
config,
|
|
154
|
+
userAfterSpecHandlers,
|
|
155
|
+
userAfterRunHandlers,
|
|
156
|
+
cleanupWrapper,
|
|
157
|
+
registered: false,
|
|
158
|
+
configPromise: undefined,
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
setupNodeEventsCh.publish(payload)
|
|
162
|
+
|
|
163
|
+
if (!payload.registered) {
|
|
164
|
+
registerNoopHandlers()
|
|
165
|
+
return config
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return payload.configPromise || config
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* @param {Function|undefined} originalSetupNodeEvents
|
|
173
|
+
* @returns {Function}
|
|
174
|
+
*/
|
|
175
|
+
function wrapSetupNodeEvents (originalSetupNodeEvents) {
|
|
176
|
+
return function ddSetupNodeEvents (on, config) {
|
|
177
|
+
const userAfterSpecHandlers = []
|
|
178
|
+
const userAfterRunHandlers = []
|
|
179
|
+
|
|
180
|
+
const wrappedOn = (event, handler) => {
|
|
181
|
+
if (event === 'after:spec') {
|
|
182
|
+
userAfterSpecHandlers.push(handler)
|
|
183
|
+
} else if (event === 'after:run') {
|
|
184
|
+
userAfterRunHandlers.push(handler)
|
|
185
|
+
} else {
|
|
186
|
+
on(event, handler)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const maybePromise = originalSetupNodeEvents
|
|
191
|
+
? originalSetupNodeEvents.call(this, wrappedOn, config)
|
|
192
|
+
: undefined
|
|
193
|
+
|
|
194
|
+
if (maybePromise && typeof maybePromise.then === 'function') {
|
|
195
|
+
return maybePromise.then((result) => {
|
|
196
|
+
return registerDdTraceHooks(
|
|
197
|
+
on,
|
|
198
|
+
mergeReturnedConfig(config, result),
|
|
199
|
+
userAfterSpecHandlers,
|
|
200
|
+
userAfterRunHandlers
|
|
201
|
+
)
|
|
202
|
+
})
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return registerDdTraceHooks(
|
|
206
|
+
on,
|
|
207
|
+
mergeReturnedConfig(config, maybePromise),
|
|
208
|
+
userAfterSpecHandlers,
|
|
209
|
+
userAfterRunHandlers
|
|
210
|
+
)
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* @param {object} config
|
|
216
|
+
* @returns {object}
|
|
217
|
+
*/
|
|
218
|
+
function wrapConfig (config) {
|
|
219
|
+
if (!config || config[DD_CONFIG_WRAPPED]) return config
|
|
220
|
+
config[DD_CONFIG_WRAPPED] = true
|
|
221
|
+
|
|
222
|
+
if (config.e2e) {
|
|
223
|
+
config.e2e.setupNodeEvents = wrapSetupNodeEvents(config.e2e.setupNodeEvents)
|
|
224
|
+
}
|
|
225
|
+
if (config.component) {
|
|
226
|
+
config.component.setupNodeEvents = wrapSetupNodeEvents(config.component.setupNodeEvents)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return config
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* @param {string} originalConfigFile absolute path to the original config file
|
|
234
|
+
* @returns {string} path to the generated wrapper file
|
|
235
|
+
*/
|
|
236
|
+
function createConfigWrapper (originalConfigFile) {
|
|
237
|
+
const wrapperFile = path.join(
|
|
238
|
+
path.dirname(originalConfigFile),
|
|
239
|
+
`.dd-cypress-config-${process.pid}.mjs`
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
const cypressConfigPath = require.resolve('./cypress-config')
|
|
243
|
+
|
|
244
|
+
// Always use ESM: it can import both CJS and ESM configs, so it works
|
|
245
|
+
// regardless of the original file's extension or "type": "module" in package.json.
|
|
246
|
+
// Import cypress-config.js directly (CJS default = module.exports object).
|
|
247
|
+
fs.writeFileSync(wrapperFile, [
|
|
248
|
+
`import originalConfig from ${JSON.stringify(pathToFileURL(originalConfigFile).href)}`,
|
|
249
|
+
`import cypressConfig from ${JSON.stringify(pathToFileURL(cypressConfigPath).href)}`,
|
|
250
|
+
'',
|
|
251
|
+
'export default cypressConfig.wrapConfig(originalConfig)',
|
|
252
|
+
'',
|
|
253
|
+
].join('\n'))
|
|
254
|
+
|
|
255
|
+
return wrapperFile
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Wraps the Cypress config file for a CLI start() call. When an explicit
|
|
260
|
+
* configFile is provided, creates a temp wrapper that imports the original
|
|
261
|
+
* and passes it through wrapConfig. This handles ESM configs (.mjs) and
|
|
262
|
+
* plain-object configs (without defineConfig) that can't be intercepted
|
|
263
|
+
* via the defineConfig shimmer.
|
|
264
|
+
*
|
|
265
|
+
* @param {object|undefined} options
|
|
266
|
+
* @returns {{ options: object|undefined, cleanup: Function }}
|
|
267
|
+
*/
|
|
268
|
+
function wrapCliConfigFileOptions (options) {
|
|
269
|
+
const noop = { options, cleanup: () => {} }
|
|
270
|
+
|
|
271
|
+
if (!options) return noop
|
|
272
|
+
|
|
273
|
+
const projectRoot = typeof options.project === 'string' ? options.project : process.cwd()
|
|
274
|
+
let configFilePath
|
|
275
|
+
|
|
276
|
+
if (options.configFile === false) {
|
|
277
|
+
// configFile: false means "no config file" — respect Cypress's semantics
|
|
278
|
+
return noop
|
|
279
|
+
} else if (typeof options.configFile === 'string') {
|
|
280
|
+
configFilePath = path.isAbsolute(options.configFile)
|
|
281
|
+
? options.configFile
|
|
282
|
+
: path.resolve(projectRoot, options.configFile)
|
|
283
|
+
} else {
|
|
284
|
+
// No explicit --config-file: resolve the default cypress.config.{js,ts,cjs,mjs}
|
|
285
|
+
for (const ext of ['.js', '.ts', '.cjs', '.mjs']) {
|
|
286
|
+
const candidate = path.join(projectRoot, `cypress.config${ext}`)
|
|
287
|
+
if (fs.existsSync(candidate)) {
|
|
288
|
+
configFilePath = candidate
|
|
289
|
+
break
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Skip .ts files — Cypress transpiles them internally via its own loader.
|
|
295
|
+
// The ESM wrapper can't import .ts directly. The defineConfig shimmer
|
|
296
|
+
// handles .ts configs since they're transpiled to CJS by Cypress.
|
|
297
|
+
if (!configFilePath || !fs.existsSync(configFilePath) || path.extname(configFilePath) === '.ts') return noop
|
|
298
|
+
|
|
299
|
+
try {
|
|
300
|
+
const wrapperFile = createConfigWrapper(configFilePath)
|
|
301
|
+
|
|
302
|
+
return {
|
|
303
|
+
options: { ...options, configFile: wrapperFile },
|
|
304
|
+
cleanup: () => {
|
|
305
|
+
try { fs.unlinkSync(wrapperFile) } catch { /* best effort */ }
|
|
306
|
+
},
|
|
307
|
+
}
|
|
308
|
+
} catch {
|
|
309
|
+
// Config directory may be read-only — fall back to no wrapping.
|
|
310
|
+
// The defineConfig shimmer will still handle configs that use defineConfig.
|
|
311
|
+
return noop
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
module.exports = {
|
|
316
|
+
wrapCliConfigFileOptions,
|
|
317
|
+
wrapConfig,
|
|
318
|
+
}
|
|
@@ -1,11 +1,93 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const shimmer = require('../../datadog-shimmer')
|
|
3
4
|
const { DD_MAJOR } = require('../../../version')
|
|
4
5
|
const { addHook } = require('./helpers/instrument')
|
|
6
|
+
const {
|
|
7
|
+
wrapCliConfigFileOptions,
|
|
8
|
+
wrapConfig,
|
|
9
|
+
} = require('./cypress-config')
|
|
5
10
|
|
|
6
|
-
//
|
|
7
|
-
//
|
|
11
|
+
// Wrap defineConfig() so configs are instrumented when loaded in Cypress's
|
|
12
|
+
// config child process. This covers both CLI and programmatic usage with CJS configs.
|
|
8
13
|
addHook({
|
|
9
14
|
name: 'cypress',
|
|
10
|
-
versions:
|
|
11
|
-
},
|
|
15
|
+
versions: ['>=10.2.0'],
|
|
16
|
+
}, (cypress) => {
|
|
17
|
+
if (typeof cypress.defineConfig === 'function') {
|
|
18
|
+
shimmer.wrap(cypress, 'defineConfig', (defineConfig) => function (config) {
|
|
19
|
+
wrapConfig(config)
|
|
20
|
+
return defineConfig(config)
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
return cypress
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
// Wrap the CLI entry points (cypress run / cypress open) to handle config files
|
|
27
|
+
// that can't be intercepted via the defineConfig shimmer: ESM configs (.mjs)
|
|
28
|
+
// and plain-object configs (without defineConfig).
|
|
29
|
+
function getCliStartWrapper (start) {
|
|
30
|
+
return function ddTraceCliStart (options) {
|
|
31
|
+
const { options: wrappedOptions, cleanup } = wrapCliConfigFileOptions(options)
|
|
32
|
+
const result = start.call(this, wrappedOptions)
|
|
33
|
+
|
|
34
|
+
if (result && typeof result.then === 'function') {
|
|
35
|
+
return result.finally(cleanup)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
cleanup()
|
|
39
|
+
return result
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Wraps `start` on an object (or its `.default`) if present.
|
|
45
|
+
*
|
|
46
|
+
* @param {object} mod module exports
|
|
47
|
+
* @returns {object} mod
|
|
48
|
+
*/
|
|
49
|
+
function wrapStartOnModule (mod) {
|
|
50
|
+
const target = mod.default || mod
|
|
51
|
+
if (typeof target.start === 'function') {
|
|
52
|
+
shimmer.wrap(target, 'start', getCliStartWrapper)
|
|
53
|
+
}
|
|
54
|
+
return mod
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Hook the CLI entry points where Cypress resolves and executes `run`/`open`.
|
|
58
|
+
// Cypress 10-14: lib/exec/{run,open}.js as separate files.
|
|
59
|
+
// Cypress 15-15.10: dist/exec/{run,open}.js as separate files.
|
|
60
|
+
// Cypress >=15.11: bundled into dist/cli-<hash>.js exporting runModule/openModule.
|
|
61
|
+
for (const file of ['lib/exec/run.js', 'lib/exec/open.js', 'dist/exec/run.js', 'dist/exec/open.js']) {
|
|
62
|
+
addHook({
|
|
63
|
+
name: 'cypress',
|
|
64
|
+
versions: ['>=10.2.0'],
|
|
65
|
+
file,
|
|
66
|
+
}, wrapStartOnModule)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Cypress >=15.11 bundles run/open into a single CLI chunk (dist/cli-<hash>.js).
|
|
70
|
+
// The chunk exports runModule and openModule, each with a start() method.
|
|
71
|
+
addHook({
|
|
72
|
+
name: 'cypress',
|
|
73
|
+
versions: ['>=10.2.0'],
|
|
74
|
+
filePattern: 'dist/cli.*',
|
|
75
|
+
}, (cliChunk) => {
|
|
76
|
+
if (cliChunk.runModule?.start) {
|
|
77
|
+
shimmer.wrap(cliChunk.runModule, 'start', getCliStartWrapper)
|
|
78
|
+
}
|
|
79
|
+
if (cliChunk.openModule?.start) {
|
|
80
|
+
shimmer.wrap(cliChunk.openModule, 'start', getCliStartWrapper)
|
|
81
|
+
}
|
|
82
|
+
return cliChunk
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
// Cypress <10 uses the old pluginsFile approach. No auto-instrumentation;
|
|
86
|
+
// users must use the manual dd-trace/ci/cypress/plugin setup.
|
|
87
|
+
// This hook is kept so the plugin system registers Cypress for version tracking.
|
|
88
|
+
if (DD_MAJOR < 6) {
|
|
89
|
+
addHook({
|
|
90
|
+
name: 'cypress',
|
|
91
|
+
versions: ['>=6.7.0 <10.2.0'],
|
|
92
|
+
}, lib => lib)
|
|
93
|
+
}
|
|
@@ -18,9 +18,8 @@ const rrtypes = {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
const rrtypeMap = new WeakMap()
|
|
21
|
-
const names = ['dns', 'node:dns']
|
|
22
21
|
|
|
23
|
-
addHook({ name:
|
|
22
|
+
addHook({ name: 'dns' }, dns => {
|
|
24
23
|
shimmer.wrap(dns, 'lookup', fn => wrap('apm:dns:lookup', fn, 2))
|
|
25
24
|
shimmer.wrap(dns, 'lookupService', fn => wrap('apm:dns:lookup_service', fn, 2))
|
|
26
25
|
shimmer.wrap(dns, 'resolve', fn => wrap('apm:dns:resolve', fn, 2))
|
|
@@ -146,7 +146,7 @@ function wrapAppUse (use) {
|
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
-
addHook({ name: 'express', versions: ['>=4'], file:
|
|
149
|
+
addHook({ name: 'express', versions: ['>=4'], file: 'lib/express.js' }, express => {
|
|
150
150
|
shimmer.wrap(express.application, 'handle', wrapHandle)
|
|
151
151
|
shimmer.wrap(express.application, 'all', wrapAppAll)
|
|
152
152
|
shimmer.wrap(express.application, 'route', wrapAppRoute)
|
|
@@ -224,19 +224,19 @@ function wrapProcessParamsMethod (requestPositionInArguments) {
|
|
|
224
224
|
}
|
|
225
225
|
}
|
|
226
226
|
|
|
227
|
-
addHook({ name: 'express', versions: ['>=4.0.0 <4.3.0'], file:
|
|
227
|
+
addHook({ name: 'express', versions: ['>=4.0.0 <4.3.0'], file: 'lib/express.js' }, express => {
|
|
228
228
|
shimmer.wrap(express.Router, 'process_params', wrapProcessParamsMethod(1))
|
|
229
229
|
return express
|
|
230
230
|
})
|
|
231
231
|
|
|
232
|
-
addHook({ name: 'express', versions: ['>=4.3.0 <5.0.0'], file:
|
|
232
|
+
addHook({ name: 'express', versions: ['>=4.3.0 <5.0.0'], file: 'lib/express.js' }, express => {
|
|
233
233
|
shimmer.wrap(express.Router, 'process_params', wrapProcessParamsMethod(2))
|
|
234
234
|
return express
|
|
235
235
|
})
|
|
236
236
|
|
|
237
237
|
const queryReadCh = channel('datadog:express:query:finish')
|
|
238
238
|
|
|
239
|
-
addHook({ name: 'express', file:
|
|
239
|
+
addHook({ name: 'express', file: 'lib/request.js', versions: ['>=5.0.0'] }, request => {
|
|
240
240
|
shimmer.wrap(request, 'query', function (originalGet) {
|
|
241
241
|
return function wrappedGet () {
|
|
242
242
|
const query = originalGet.call(this)
|
|
@@ -84,37 +84,35 @@ const paramsByFileHandleMethods = {
|
|
|
84
84
|
writeFile: ['data', 'options'],
|
|
85
85
|
writev: ['buffers', 'position'],
|
|
86
86
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
wrap(fs, '
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
wrap(fs.Dir.prototype, Symbol.asyncIterator, createWrapDirAsyncIterator())
|
|
109
|
-
}
|
|
87
|
+
addHook({ name: 'fs' }, fs => {
|
|
88
|
+
const asyncMethods = Object.keys(paramsByMethod)
|
|
89
|
+
const syncMethods = asyncMethods.map(name => `${name}Sync`)
|
|
90
|
+
|
|
91
|
+
massWrap(fs, asyncMethods, createWrapFunction())
|
|
92
|
+
massWrap(fs, syncMethods, createWrapFunction())
|
|
93
|
+
massWrap(fs.promises, asyncMethods, createWrapFunction('promises.'))
|
|
94
|
+
|
|
95
|
+
wrap(fs.realpath, 'native', createWrapFunction('', 'realpath.native'))
|
|
96
|
+
wrap(fs.realpathSync, 'native', createWrapFunction('', 'realpath.native'))
|
|
97
|
+
wrap(fs.promises.realpath, 'native', createWrapFunction('', 'realpath.native'))
|
|
98
|
+
|
|
99
|
+
wrap(fs, 'createReadStream', wrapCreateStream)
|
|
100
|
+
wrap(fs, 'createWriteStream', wrapCreateStream)
|
|
101
|
+
if (fs.Dir) {
|
|
102
|
+
wrap(fs.Dir.prototype, 'close', createWrapFunction('dir.'))
|
|
103
|
+
wrap(fs.Dir.prototype, 'closeSync', createWrapFunction('dir.'))
|
|
104
|
+
wrap(fs.Dir.prototype, 'read', createWrapFunction('dir.'))
|
|
105
|
+
wrap(fs.Dir.prototype, 'readSync', createWrapFunction('dir.'))
|
|
106
|
+
wrap(fs.Dir.prototype, Symbol.asyncIterator, createWrapDirAsyncIterator())
|
|
107
|
+
}
|
|
110
108
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
109
|
+
wrap(fs, 'unwatchFile', createWatchWrapFunction())
|
|
110
|
+
wrap(fs, 'watch', createWatchWrapFunction())
|
|
111
|
+
wrap(fs, 'watchFile', createWatchWrapFunction())
|
|
112
|
+
|
|
113
|
+
return fs
|
|
114
|
+
})
|
|
114
115
|
|
|
115
|
-
return fs
|
|
116
|
-
})
|
|
117
|
-
}
|
|
118
116
|
function isFirstMethodReturningFileHandle (original) {
|
|
119
117
|
return !kHandle && original.name === 'open'
|
|
120
118
|
}
|
|
@@ -45,45 +45,73 @@ if (!dc.unsubscribe) {
|
|
|
45
45
|
dc.unsubscribe = (channel, cb) => {
|
|
46
46
|
if (dc.channel(channel).hasSubscribers) {
|
|
47
47
|
dc.channel(channel).unsubscribe(cb)
|
|
48
|
+
return true
|
|
48
49
|
}
|
|
50
|
+
return false
|
|
49
51
|
}
|
|
50
52
|
}
|
|
51
53
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
/**
|
|
55
|
+
* @param {string} name
|
|
56
|
+
*/
|
|
57
|
+
function doHook (name) {
|
|
58
|
+
const hook = hooks[name] ?? hooks[`node:${name}`]
|
|
54
59
|
if (!hook) {
|
|
55
|
-
log.error('esbuild-wrapped %s missing in list of hooks',
|
|
60
|
+
log.error('esbuild-wrapped %s missing in list of hooks', name)
|
|
56
61
|
return
|
|
57
62
|
}
|
|
58
63
|
|
|
59
64
|
const hookFn = hook.fn ?? hook
|
|
60
65
|
if (typeof hookFn !== 'function') {
|
|
61
|
-
log.error('esbuild-wrapped hook %s is not a function',
|
|
66
|
+
log.error('esbuild-wrapped hook %s is not a function', name)
|
|
62
67
|
return
|
|
63
68
|
}
|
|
64
69
|
|
|
65
70
|
try {
|
|
66
71
|
hookFn()
|
|
67
72
|
} catch {
|
|
68
|
-
log.error('esbuild-wrapped %s hook failed',
|
|
73
|
+
log.error('esbuild-wrapped %s hook failed', name)
|
|
69
74
|
}
|
|
70
75
|
}
|
|
71
76
|
|
|
72
|
-
|
|
73
|
-
|
|
77
|
+
/** @type {Set<string>} */
|
|
78
|
+
const instrumentedNodeModules = new Set()
|
|
74
79
|
|
|
75
|
-
|
|
76
|
-
|
|
80
|
+
/** @typedef {{ package: string, module: unknown, version: string, path: string }} Payload */
|
|
81
|
+
dc.subscribe(CHANNEL, (message) => {
|
|
82
|
+
const payload = /** @type {Payload} */ (message)
|
|
83
|
+
const name = payload.package
|
|
84
|
+
|
|
85
|
+
const isPrefixedWithNode = name.startsWith('node:')
|
|
86
|
+
|
|
87
|
+
const isNodeModule = isPrefixedWithNode || !hooks[name]
|
|
88
|
+
|
|
89
|
+
if (isNodeModule) {
|
|
90
|
+
const nodeName = isPrefixedWithNode ? name.slice(5) : name
|
|
91
|
+
// Used for node: prefixed modules to prevent double instrumentation.
|
|
92
|
+
if (instrumentedNodeModules.has(nodeName)) {
|
|
93
|
+
return
|
|
94
|
+
}
|
|
95
|
+
instrumentedNodeModules.add(nodeName)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
doHook(name)
|
|
99
|
+
|
|
100
|
+
const instrumentation = instrumentations[name] ?? instrumentations[`node:${name}`]
|
|
101
|
+
|
|
102
|
+
if (!instrumentation) {
|
|
103
|
+
log.error('esbuild-wrapped %s missing in list of instrumentations', name)
|
|
77
104
|
return
|
|
78
105
|
}
|
|
79
106
|
|
|
80
|
-
for (const {
|
|
81
|
-
if (payload.path !== filename(name, file))
|
|
82
|
-
|
|
107
|
+
for (const { file, versions, hook } of instrumentation) {
|
|
108
|
+
if (payload.path !== filename(name, file) || !matchVersion(payload.version, versions)) {
|
|
109
|
+
continue
|
|
110
|
+
}
|
|
83
111
|
|
|
84
112
|
try {
|
|
85
113
|
loadChannel.publish({ name, version: payload.version, file })
|
|
86
|
-
payload.module = hook(payload.module, payload.version)
|
|
114
|
+
payload.module = hook(payload.module, payload.version) ?? payload.module
|
|
87
115
|
} catch (e) {
|
|
88
116
|
log.error('Error executing bundler hook', e)
|
|
89
117
|
}
|