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
package/README.md
CHANGED
|
@@ -93,6 +93,21 @@ Regardless of where you open the issue, someone at Datadog will try to help.
|
|
|
93
93
|
|
|
94
94
|
If you would like to trace your bundled application then please read this page on [bundling and dd-trace](https://docs.datadoghq.com/tracing/trace_collection/automatic_instrumentation/dd_libraries/nodejs/#bundling). It includes information on how to use our ESBuild plugin and includes caveats for other bundlers.
|
|
95
95
|
|
|
96
|
+
Next.js 15.5 or newer applications that use Turbopack can wrap their existing configuration:
|
|
97
|
+
|
|
98
|
+
```javascript
|
|
99
|
+
const { withDatadogTurbopack } = require('dd-trace/next')
|
|
100
|
+
|
|
101
|
+
const nextConfig = { reactStrictMode: true }
|
|
102
|
+
|
|
103
|
+
module.exports = withDatadogTurbopack(nextConfig)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Preload `dd-trace/init` before the application loads. The wrapper instruments CommonJS dependencies and supported
|
|
107
|
+
source-rewrite targets in Node.js bundles.
|
|
108
|
+
|
|
109
|
+
The wrapper does not instrument client or edge bundles. It also excludes generic ESM export replacement, linked
|
|
110
|
+
workspaces, Prisma clients outside `node_modules`, and Yarn Plug'n'Play packages.
|
|
96
111
|
|
|
97
112
|
## Security Vulnerabilities
|
|
98
113
|
|
|
@@ -282,7 +282,9 @@ function readProfileDefinitions (filename) {
|
|
|
282
282
|
|
|
283
283
|
function getJavascriptProfileDefinitions (source) {
|
|
284
284
|
const syntax = maskJavascriptCommentsAndStrings(source)
|
|
285
|
-
const exports = [...syntax.matchAll(
|
|
285
|
+
const exports = [...syntax.matchAll(
|
|
286
|
+
/(?:^|\S[\t\v\f\p{Zs}\uFEFF]*[\n\r\u2028\u2029])\s*(?:module\s*\.\s*exports\s*=|export\s+default)\s*\{/gu
|
|
287
|
+
)]
|
|
286
288
|
if (exports.length === 0) return new Map()
|
|
287
289
|
if (exports.length !== 1) throw new Error('configuration must export one literal profile object')
|
|
288
290
|
|
|
@@ -492,7 +494,7 @@ function getYamlStringProfileDefinition (source) {
|
|
|
492
494
|
const value = source.trimStart()
|
|
493
495
|
const quote = value[0]
|
|
494
496
|
if (quote !== '"' && quote !== "'") {
|
|
495
|
-
const comment =
|
|
497
|
+
const comment = /[ \t]#/.exec(value)
|
|
496
498
|
const definition = value.slice(0, comment?.index ?? value.length).trimEnd()
|
|
497
499
|
return definition || undefined
|
|
498
500
|
}
|
|
@@ -88,7 +88,10 @@ function getFailureDetail (result) {
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
function sanitizeLine (value) {
|
|
91
|
-
|
|
91
|
+
const line = sanitizeString(String(value)).replaceAll(/\p{Cc}+/gu, ' ').trim().slice(0, 1000)
|
|
92
|
+
let end = line.length
|
|
93
|
+
while (end > 0 && line[end - 1] === '.') end--
|
|
94
|
+
return line.slice(0, end)
|
|
92
95
|
}
|
|
93
96
|
|
|
94
97
|
module.exports = { checkInstalledPackage, getInstalledPackageFailure }
|
|
@@ -442,7 +442,7 @@ function hasUnavailableRemoteCiCommand (ci) {
|
|
|
442
442
|
})) return true
|
|
443
443
|
|
|
444
444
|
return evidence.some(item => {
|
|
445
|
-
const reference =
|
|
445
|
+
const reference = /^[ \t]*uses:[ \t]*["']?([^"'\s]+)["']?[ \t]*$/im.exec(item)?.[1]
|
|
446
446
|
return reference && !reference.startsWith('./')
|
|
447
447
|
})
|
|
448
448
|
}
|
package/index.d.ts
CHANGED
|
@@ -466,6 +466,13 @@ declare namespace tracer {
|
|
|
466
466
|
* Maximum number of traces matching this rule to sample per second.
|
|
467
467
|
*/
|
|
468
468
|
maxPerSecond?: number
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* When `true`, a trace chunk rejected by this rule is fully dropped:
|
|
472
|
+
* it is excluded from client-side stats and never sent to the Agent.
|
|
473
|
+
* @default false
|
|
474
|
+
*/
|
|
475
|
+
discard?: boolean
|
|
469
476
|
}
|
|
470
477
|
|
|
471
478
|
/**
|
|
@@ -650,6 +657,7 @@ declare namespace tracer {
|
|
|
650
657
|
* Sampling rules to apply to priority sampling. Each rule matches against a trace's
|
|
651
658
|
* `service`, `name`, `resource`, and `tags`, and applies the rule's `sampleRate`. Use a
|
|
652
659
|
* `sampleRate` of `0` to drop matching traces (for example to filter out unwanted resources).
|
|
660
|
+
* Specify `"discard": true` to fully drop it from stats as well.
|
|
653
661
|
* If not specified, will defer to global sampling rate for all spans.
|
|
654
662
|
* @default []
|
|
655
663
|
* @env DD_TRACE_SAMPLING_RULES
|
|
@@ -3279,6 +3287,10 @@ declare namespace tracer {
|
|
|
3279
3287
|
}
|
|
3280
3288
|
|
|
3281
3289
|
export namespace opentelemetry {
|
|
3290
|
+
export interface MeterProvider {
|
|
3291
|
+
shutdown(callback?: (error: Error | null) => void): void;
|
|
3292
|
+
}
|
|
3293
|
+
|
|
3282
3294
|
/**
|
|
3283
3295
|
* A registry for creating named {@link Tracer}s.
|
|
3284
3296
|
*/
|
package/next.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { NextConfig } from 'next'
|
|
2
|
+
|
|
3
|
+
type NextConfigValue = NextConfig | undefined
|
|
4
|
+
|
|
5
|
+
export function withDatadogTurbopack<TArguments extends unknown[]> (
|
|
6
|
+
nextConfig: (...args: TArguments) => Promise<NextConfigValue>
|
|
7
|
+
): (...args: TArguments) => Promise<NextConfig>
|
|
8
|
+
|
|
9
|
+
export function withDatadogTurbopack<TArguments extends unknown[]> (
|
|
10
|
+
nextConfig: (...args: TArguments) => NextConfigValue
|
|
11
|
+
): (...args: TArguments) => NextConfig
|
|
12
|
+
|
|
13
|
+
export function withDatadogTurbopack<TArguments extends unknown[]> (
|
|
14
|
+
nextConfig: (...args: TArguments) => NextConfigValue | Promise<NextConfigValue>
|
|
15
|
+
): (...args: TArguments) => NextConfig | Promise<NextConfig>
|
|
16
|
+
|
|
17
|
+
export function withDatadogTurbopack (
|
|
18
|
+
nextConfig: Promise<NextConfigValue>
|
|
19
|
+
): (phase?: string) => Promise<NextConfig>
|
|
20
|
+
|
|
21
|
+
export function withDatadogTurbopack (
|
|
22
|
+
nextConfig?: NextConfigValue
|
|
23
|
+
): (phase?: string) => NextConfig
|
|
24
|
+
|
|
25
|
+
export function withDatadogTurbopack (
|
|
26
|
+
nextConfig: NextConfigValue | Promise<NextConfigValue>
|
|
27
|
+
): (phase?: string) => NextConfig | Promise<NextConfig>
|
package/next.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dd-trace",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.16.0",
|
|
4
4
|
"description": "Datadog APM tracing client for JavaScript",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"typings": "index.d.ts",
|
|
@@ -22,12 +22,11 @@
|
|
|
22
22
|
"type:check": "tsc --noEmit -p tsconfig.dev.json",
|
|
23
23
|
"type:doc:build": "cd docs && yarn && yarn build",
|
|
24
24
|
"type:doc:test": "cd docs && yarn && yarn test",
|
|
25
|
-
"lint": "
|
|
25
|
+
"lint": "bash ./scripts/lint.sh",
|
|
26
26
|
"lint:fix": "node scripts/check_licenses.js && node scripts/check-agents-md-size.js && node scripts/check-no-coverage-artifacts.js && node scripts/check-no-mcr-images.js && node scripts/check-docker-image-shas.js && eslint . --concurrency=auto --max-warnings 0 --fix",
|
|
27
27
|
"verify:carrier-fields": "node scripts/verify-carrier-fields.mjs",
|
|
28
28
|
"lint:inspect": "npx @eslint/config-inspector@latest",
|
|
29
29
|
"lint:codeowners": "codeowners-audit",
|
|
30
|
-
"lint:codeowners:ci": "codeowners-audit --glob='**/*.spec.js' --glob='benchmark/sirun/**' --glob='.agents/**' --glob='.claude/**' --glob='integration-tests/**' --glob='packages/dd-trace/src/guardrails/**'",
|
|
31
30
|
"lint:editorconfig": "docker build -q -t ec .github/editorconfig-checker && docker run --rm --volume=$PWD:/check ec",
|
|
32
31
|
"release:proposal": "node scripts/release/proposal",
|
|
33
32
|
"services": "node ./scripts/install_plugin_modules && node packages/dd-trace/test/setup/services",
|
|
@@ -47,6 +46,8 @@
|
|
|
47
46
|
"test:trace:guardrails:ci": "node scripts/c8-ci.js test:trace:guardrails",
|
|
48
47
|
"test:esbuild": "mocha \"packages/datadog-esbuild/test/**/*.spec.js\"",
|
|
49
48
|
"test:esbuild:ci": "node scripts/c8-ci.js test:esbuild",
|
|
49
|
+
"test:turbopack": "mocha \"packages/datadog-turbopack/test/**/*.spec.js\"",
|
|
50
|
+
"test:turbopack:ci": "node scripts/c8-ci.js test:turbopack",
|
|
50
51
|
"test:webpack": "mocha \"packages/datadog-webpack/test/**/*.spec.js\"",
|
|
51
52
|
"test:webpack:ci": "node scripts/c8-ci.js test:webpack",
|
|
52
53
|
"test:instrumentations": "mocha --fail-zero \"packages/datadog-instrumentations/test/@(${PLUGINS}).spec.js\"",
|
|
@@ -91,6 +92,7 @@
|
|
|
91
92
|
"test:integration:electron": "mocha \"integration-tests/electron/*.spec.js\"",
|
|
92
93
|
"test:integration:esbuild": "mocha --timeout 60000 \"integration-tests/esbuild/*.spec.js\"",
|
|
93
94
|
"test:integration:esbuild:coverage": "node ./integration-tests/coverage/run-suite.js --timeout 60000 \"integration-tests/esbuild/*.spec.js\"",
|
|
95
|
+
"test:integration:turbopack": "mocha --timeout 60000 \"integration-tests/turbopack/*.spec.js\"",
|
|
94
96
|
"test:integration:webpack": "mocha --timeout 60000 \"integration-tests/webpack/*.spec.js\"",
|
|
95
97
|
"test:integration:openfeature": "mocha --timeout 60000 \"integration-tests/openfeature/*.spec.js\"",
|
|
96
98
|
"test:integration:openfeature:coverage": "node ./integration-tests/coverage/run-suite.js --timeout 60000 \"integration-tests/openfeature/*.spec.js\"",
|
|
@@ -150,6 +152,8 @@
|
|
|
150
152
|
"ci/**/*",
|
|
151
153
|
"cypress/**/*",
|
|
152
154
|
"esbuild.js",
|
|
155
|
+
"next.js",
|
|
156
|
+
"next.d.ts",
|
|
153
157
|
"webpack.js",
|
|
154
158
|
"ext/**/*",
|
|
155
159
|
"index.d.ts",
|
|
@@ -179,12 +183,12 @@
|
|
|
179
183
|
],
|
|
180
184
|
"dependencies": {
|
|
181
185
|
"dc-polyfill": "^0.1.11",
|
|
182
|
-
"import-in-the-middle": "
|
|
186
|
+
"import-in-the-middle": "3.5.1",
|
|
183
187
|
"opentracing": ">=0.14.7"
|
|
184
188
|
},
|
|
185
189
|
"optionalDependencies": {
|
|
186
|
-
"@datadog/libdatadog": "^0.
|
|
187
|
-
"@datadog/libdatadog-extras": "^0.
|
|
190
|
+
"@datadog/libdatadog": "^0.21.0",
|
|
191
|
+
"@datadog/libdatadog-extras": "^0.21.0",
|
|
188
192
|
"@datadog/native-appsec": "11.0.2",
|
|
189
193
|
"@datadog/native-iast-taint-tracking": "4.2.1",
|
|
190
194
|
"@datadog/native-metrics": "4.0.0",
|
|
@@ -198,9 +202,9 @@
|
|
|
198
202
|
"@actions/core": "^3.0.1",
|
|
199
203
|
"@actions/github": "^9.1.1",
|
|
200
204
|
"@babel/helpers": "^8.0.0",
|
|
201
|
-
"@eslint/eslintrc": "^3.3.
|
|
205
|
+
"@eslint/eslintrc": "^3.3.7",
|
|
202
206
|
"@eslint/js": "^10.0.1",
|
|
203
|
-
"@eslint/plugin-kit": "^0.7.
|
|
207
|
+
"@eslint/plugin-kit": "^0.7.3",
|
|
204
208
|
"@datadog/openfeature-node-server": "2.2.0",
|
|
205
209
|
"@msgpack/msgpack": "^3.1.3",
|
|
206
210
|
"@openfeature/core": "^1.12.0",
|
|
@@ -220,16 +224,16 @@
|
|
|
220
224
|
"eslint": "^10.9.1",
|
|
221
225
|
"eslint-plugin-cypress": "^7.0.1",
|
|
222
226
|
"eslint-plugin-import-x": "^4.16.2",
|
|
223
|
-
"eslint-plugin-jsdoc": "^64.
|
|
227
|
+
"eslint-plugin-jsdoc": "^64.3.5",
|
|
224
228
|
"eslint-plugin-mocha": "^12.0.2",
|
|
225
229
|
"eslint-plugin-n": "^18.3.0",
|
|
226
230
|
"eslint-plugin-promise": "^7.3.0",
|
|
227
|
-
"eslint-plugin-regexp": "^3.
|
|
231
|
+
"eslint-plugin-regexp": "^3.2.0",
|
|
228
232
|
"eslint-plugin-sonarjs": "^4.2.0",
|
|
229
233
|
"eslint-plugin-unicorn": "^74.0.0",
|
|
230
234
|
"express": "^5.1.0",
|
|
231
235
|
"glob": "^10.4.5",
|
|
232
|
-
"globals": "^17.
|
|
236
|
+
"globals": "^17.12.0",
|
|
233
237
|
"graphql": "*",
|
|
234
238
|
"husky": "^9.1.7",
|
|
235
239
|
"istanbul-lib-coverage": "^3.2.2",
|
|
@@ -241,12 +245,12 @@
|
|
|
241
245
|
"mocha": "^11.8.0",
|
|
242
246
|
"mocha-junit-reporter": "^2.2.1",
|
|
243
247
|
"mocha-multi-reporters": "^1.5.1",
|
|
244
|
-
"multer": "^2.
|
|
248
|
+
"multer": "^2.3.0",
|
|
245
249
|
"nock": "^14.0.17",
|
|
246
250
|
"node-preload": "^0.2.1",
|
|
247
251
|
"nyc": "^18.0.0",
|
|
248
252
|
"octokit": "^5.0.3",
|
|
249
|
-
"p-limit": "^7.3.
|
|
253
|
+
"p-limit": "^7.3.2",
|
|
250
254
|
"proxyquire": "^2.1.3",
|
|
251
255
|
"retry": "^0.13.1",
|
|
252
256
|
"semifies": "^1.0.0",
|
|
@@ -8,8 +8,9 @@ const { pathToFileURL, fileURLToPath } = require('node:url')
|
|
|
8
8
|
const instrumentations = require('../datadog-instrumentations/src/helpers/instrumentations')
|
|
9
9
|
const extractPackageAndModulePath = require('../datadog-instrumentations/src/helpers/extract-package-and-module-path')
|
|
10
10
|
const hooks = require('../datadog-instrumentations/src/helpers/hooks')
|
|
11
|
-
const { processModule, isESMFile } = require('./src/utils')
|
|
12
11
|
const log = require('./src/log')
|
|
12
|
+
const { createEsmResolver } = require('./src/resolver')
|
|
13
|
+
const { isESMFile, processModule } = require('./src/utils')
|
|
13
14
|
|
|
14
15
|
const ESM_INTERCEPTED_SUFFIX = '._dd_esbuild_intercepted'
|
|
15
16
|
const INTERNAL_ESM_INTERCEPTED_PREFIX = '/_dd_esm_internal_/'
|
|
@@ -166,6 +167,13 @@ ${build.initialOptions.banner.js}`
|
|
|
166
167
|
|
|
167
168
|
// first time is intercepted, proxy should be created, next time the original should be loaded
|
|
168
169
|
const interceptedESMModules = new Set()
|
|
170
|
+
let resolver
|
|
171
|
+
|
|
172
|
+
build.onEnd(async () => {
|
|
173
|
+
const activeResolver = resolver
|
|
174
|
+
resolver = undefined
|
|
175
|
+
await activeResolver?.close()
|
|
176
|
+
})
|
|
169
177
|
|
|
170
178
|
build.onResolve({ filter: /.*/ }, args => {
|
|
171
179
|
if (externalModules.has(args.path)) {
|
|
@@ -316,10 +324,15 @@ ${build.initialOptions.banner.js}`
|
|
|
316
324
|
|
|
317
325
|
interceptedESMModules.add(args.path)
|
|
318
326
|
|
|
327
|
+
resolver ??= createEsmResolver()
|
|
319
328
|
const setters = await processModule({
|
|
320
329
|
path: args.path,
|
|
321
330
|
internal: data.internal,
|
|
322
331
|
context: { format: 'module' },
|
|
332
|
+
excludeDefault: false,
|
|
333
|
+
moduleSources: new Map(),
|
|
334
|
+
resolver,
|
|
335
|
+
transform: build.esbuild.transformSync,
|
|
323
336
|
})
|
|
324
337
|
|
|
325
338
|
const iitmPath = require.resolve('import-in-the-middle/lib/register.js')
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const childProcess = require('node:child_process')
|
|
4
|
+
const readline = require('node:readline')
|
|
5
|
+
|
|
6
|
+
const { getEnvironmentVariables } = require('../../dd-trace/src/config/helper')
|
|
7
|
+
|
|
8
|
+
const MAX_PENDING_REQUESTS = 1024
|
|
9
|
+
const MAX_STDERR_LENGTH = 16 * 1024
|
|
10
|
+
const LOAD_OPERATION = 0
|
|
11
|
+
const RESOLVE_OPERATION = 1
|
|
12
|
+
const RESOLVER_SOURCE = String.raw`
|
|
13
|
+
import { createReadStream, createWriteStream } from 'node:fs'
|
|
14
|
+
import readline from 'node:readline'
|
|
15
|
+
|
|
16
|
+
const requests = createReadStream(null, { fd: 3, autoClose: false })
|
|
17
|
+
const responses = createWriteStream(null, { fd: 4, autoClose: false })
|
|
18
|
+
const lines = readline.createInterface({ input: requests })
|
|
19
|
+
|
|
20
|
+
for await (const line of lines) {
|
|
21
|
+
let request
|
|
22
|
+
try {
|
|
23
|
+
request = JSON.parse(line)
|
|
24
|
+
const url = await import.meta.resolve(request.specifier, request.parentURL)
|
|
25
|
+
responses.write(JSON.stringify({ id: request.id, url }) + '\n')
|
|
26
|
+
} catch (error) {
|
|
27
|
+
responses.write(JSON.stringify({
|
|
28
|
+
error: {
|
|
29
|
+
code: error?.code,
|
|
30
|
+
message: String(error?.message ?? error),
|
|
31
|
+
},
|
|
32
|
+
id: request?.id,
|
|
33
|
+
}) + '\n')
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
responses.end()
|
|
37
|
+
`
|
|
38
|
+
|
|
39
|
+
/** @typedef {{ specifier: string, parentURL: string }} StarReexport */
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @typedef {{ exportNames: Iterable<string>, starReexports?: StarReexport[] }} ModuleExports
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @typedef {Set<string>|ModuleExports} GetExportsValue
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @typedef {[typeof LOAD_OPERATION, URL, object] | [typeof RESOLVE_OPERATION, string, object]} GetExportsOperation
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @typedef {{ done: false, value: GetExportsOperation } | { done: true, value: GetExportsValue }} GetExportsResult
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* @typedef {{
|
|
59
|
+
* next: (value?: unknown) => GetExportsResult,
|
|
60
|
+
* throw: (error?: unknown) => GetExportsResult,
|
|
61
|
+
* }} GetExportsGenerator
|
|
62
|
+
*/
|
|
63
|
+
|
|
64
|
+
class EsmResolver {
|
|
65
|
+
#child
|
|
66
|
+
#closed = false
|
|
67
|
+
#closedPromise
|
|
68
|
+
#failure
|
|
69
|
+
#nextId = 0
|
|
70
|
+
#pending = new Map()
|
|
71
|
+
#readline
|
|
72
|
+
#rejectClosed
|
|
73
|
+
#request
|
|
74
|
+
#resolveClosed
|
|
75
|
+
#stderr = ''
|
|
76
|
+
|
|
77
|
+
constructor () {
|
|
78
|
+
this.#closedPromise = new Promise((resolve, reject) => {
|
|
79
|
+
this.#resolveClosed = resolve
|
|
80
|
+
this.#rejectClosed = reject
|
|
81
|
+
})
|
|
82
|
+
// Mark lifecycle failures handled immediately. close() still returns the original promise.
|
|
83
|
+
this.#closedPromise.catch(() => {})
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* @param {string} specifier
|
|
88
|
+
* @param {URL|string} parentURL
|
|
89
|
+
* @returns {Promise<string>}
|
|
90
|
+
*/
|
|
91
|
+
resolve (specifier, parentURL) {
|
|
92
|
+
if (this.#closed) return Promise.reject(this.#failure ?? new Error('The ESM resolver is closed'))
|
|
93
|
+
if (this.#pending.size >= MAX_PENDING_REQUESTS) {
|
|
94
|
+
return Promise.reject(new Error(`The ESM resolver has more than ${MAX_PENDING_REQUESTS} pending requests`))
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
this.#start()
|
|
99
|
+
} catch (error) {
|
|
100
|
+
this.#failure = error
|
|
101
|
+
this.#closed = true
|
|
102
|
+
this.#resolveClosed()
|
|
103
|
+
return Promise.reject(error)
|
|
104
|
+
}
|
|
105
|
+
const id = this.#nextId++
|
|
106
|
+
return new Promise((resolve, reject) => {
|
|
107
|
+
this.#pending.set(id, { reject, resolve })
|
|
108
|
+
this.#request.write(`${JSON.stringify({ id, parentURL: String(parentURL), specifier })}\n`, error => {
|
|
109
|
+
if (error) this.#rejectRequest(id, error)
|
|
110
|
+
})
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** @returns {Promise<void>} */
|
|
115
|
+
close () {
|
|
116
|
+
if (!this.#closed) {
|
|
117
|
+
this.#closed = true
|
|
118
|
+
if (this.#child) this.#request.end()
|
|
119
|
+
else this.#resolveClosed()
|
|
120
|
+
}
|
|
121
|
+
return this.#closedPromise
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
#start () {
|
|
125
|
+
if (this.#child) return
|
|
126
|
+
const env = getEnvironmentVariables()
|
|
127
|
+
env.DD_CIVISIBILITY_ENABLED = 'false'
|
|
128
|
+
env.DD_INSTRUMENTATION_TELEMETRY_ENABLED = 'false'
|
|
129
|
+
env.DD_TRACE_ENABLED = 'false'
|
|
130
|
+
this.#child = childProcess.spawn(process.execPath, [
|
|
131
|
+
'--no-warnings',
|
|
132
|
+
'--experimental-import-meta-resolve',
|
|
133
|
+
'--input-type=module',
|
|
134
|
+
'--eval',
|
|
135
|
+
RESOLVER_SOURCE,
|
|
136
|
+
], {
|
|
137
|
+
env,
|
|
138
|
+
stdio: ['ignore', 'ignore', 'pipe', 'pipe', 'pipe'],
|
|
139
|
+
})
|
|
140
|
+
this.#request = this.#child.stdio[3]
|
|
141
|
+
this.#readline = readline.createInterface({ input: this.#child.stdio[4] })
|
|
142
|
+
this.#readline.on('line', this.#handleLine)
|
|
143
|
+
this.#request.once('error', this.#handleError)
|
|
144
|
+
this.#child.stdio[4].once('error', this.#handleError)
|
|
145
|
+
this.#child.stderr.on('data', this.#handleStderr)
|
|
146
|
+
this.#child.once('error', this.#handleError)
|
|
147
|
+
this.#child.once('close', this.#handleClose)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** @param {string} line */
|
|
151
|
+
#handleLine = (line) => {
|
|
152
|
+
let response
|
|
153
|
+
try {
|
|
154
|
+
response = JSON.parse(line)
|
|
155
|
+
} catch (error) {
|
|
156
|
+
this.#fail(new Error(`The ESM resolver returned malformed JSON: ${error.message}`))
|
|
157
|
+
return
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const id = response?.id
|
|
161
|
+
if (!Number.isSafeInteger(id) || !this.#pending.has(id)) {
|
|
162
|
+
this.#fail(new Error('The ESM resolver returned an unknown request identifier'))
|
|
163
|
+
return
|
|
164
|
+
}
|
|
165
|
+
if (typeof response.url === 'string' && response.error === undefined) {
|
|
166
|
+
const request = this.#pending.get(id)
|
|
167
|
+
this.#pending.delete(id)
|
|
168
|
+
request.resolve(response.url)
|
|
169
|
+
return
|
|
170
|
+
}
|
|
171
|
+
if (typeof response.error?.message !== 'string' || response.url !== undefined) {
|
|
172
|
+
this.#rejectRequest(id, new Error('The ESM resolver returned a malformed response'))
|
|
173
|
+
return
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const error = /** @type {Error & { code?: string }} */ (new Error(response.error.message))
|
|
177
|
+
if (typeof response.error.code === 'string') error.code = response.error.code
|
|
178
|
+
this.#rejectRequest(id, error)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** @param {Buffer|string} chunk */
|
|
182
|
+
#handleStderr = (chunk) => {
|
|
183
|
+
if (this.#stderr.length >= MAX_STDERR_LENGTH) return
|
|
184
|
+
this.#stderr += String(chunk).slice(0, MAX_STDERR_LENGTH - this.#stderr.length)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** @param {Error} error */
|
|
188
|
+
#handleError = (error) => {
|
|
189
|
+
this.#fail(error)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* @param {number|null} code
|
|
194
|
+
* @param {string|null} signal
|
|
195
|
+
*/
|
|
196
|
+
#handleClose = (code, signal) => {
|
|
197
|
+
this.#readline?.close()
|
|
198
|
+
this.#closed = true
|
|
199
|
+
let error = this.#failure
|
|
200
|
+
if (!error && (code !== 0 || this.#pending.size > 0)) {
|
|
201
|
+
const reason = signal ? `signal ${signal}` : `status ${code}`
|
|
202
|
+
const stderr = this.#stderr.trim()
|
|
203
|
+
error = new Error(`The ESM resolver exited with ${reason}${stderr ? `: ${stderr}` : ''}`)
|
|
204
|
+
this.#failure = error
|
|
205
|
+
}
|
|
206
|
+
if (error) {
|
|
207
|
+
this.#rejectAll(error)
|
|
208
|
+
this.#rejectClosed(error)
|
|
209
|
+
} else {
|
|
210
|
+
this.#resolveClosed()
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* @param {number} id
|
|
216
|
+
* @param {Error} error
|
|
217
|
+
*/
|
|
218
|
+
#rejectRequest (id, error) {
|
|
219
|
+
const request = this.#pending.get(id)
|
|
220
|
+
if (!request) return
|
|
221
|
+
this.#pending.delete(id)
|
|
222
|
+
request.reject(error)
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** @param {Error} error */
|
|
226
|
+
#fail (error) {
|
|
227
|
+
if (this.#failure) return
|
|
228
|
+
this.#failure = error
|
|
229
|
+
this.#closed = true
|
|
230
|
+
this.#rejectAll(error)
|
|
231
|
+
this.#request?.destroy()
|
|
232
|
+
this.#child?.kill()
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/** @param {Error} error */
|
|
236
|
+
#rejectAll (error) {
|
|
237
|
+
for (const request of this.#pending.values()) request.reject(error)
|
|
238
|
+
this.#pending.clear()
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/** @returns {EsmResolver} */
|
|
243
|
+
function createEsmResolver () {
|
|
244
|
+
return new EsmResolver()
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Drives the generator returned by import-in-the-middle >=3.1.0 export discovery.
|
|
249
|
+
*
|
|
250
|
+
* @param {GetExportsGenerator} exportsGenerator
|
|
251
|
+
* @param {(url: URL, context: object) => { source: string, format: string }} getSource
|
|
252
|
+
* @param {(specifier: string, context: object) => Promise<{ format: string, url: URL }>} resolve
|
|
253
|
+
* @returns {Promise<ModuleExports>}
|
|
254
|
+
*/
|
|
255
|
+
async function driveGetExportsGenerator (exportsGenerator, getSource, resolve) {
|
|
256
|
+
let next = exportsGenerator.next()
|
|
257
|
+
while (next.done === false) {
|
|
258
|
+
let result
|
|
259
|
+
let failure
|
|
260
|
+
let threw = false
|
|
261
|
+
|
|
262
|
+
try {
|
|
263
|
+
const operation = next.value
|
|
264
|
+
const operationType = operation[0]
|
|
265
|
+
|
|
266
|
+
if (operationType === LOAD_OPERATION) {
|
|
267
|
+
result = getSource(operation[1], operation[2])
|
|
268
|
+
} else if (operationType === RESOLVE_OPERATION) {
|
|
269
|
+
// eslint-disable-next-line no-await-in-loop
|
|
270
|
+
result = await resolve(operation[1], operation[2])
|
|
271
|
+
} else {
|
|
272
|
+
throw new Error(`Unsupported import-in-the-middle getExports operation: ${operationType}`)
|
|
273
|
+
}
|
|
274
|
+
} catch (error) {
|
|
275
|
+
threw = true
|
|
276
|
+
failure = error
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
next = threw ? exportsGenerator.throw(failure) : exportsGenerator.next(result)
|
|
280
|
+
}
|
|
281
|
+
return next.value instanceof Set ? { exportNames: next.value } : next.value
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
module.exports = { createEsmResolver, driveGetExportsGenerator }
|