dd-trace 5.73.0 → 5.75.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/LICENSE-3rdparty.csv +2 -0
  2. package/index.d.ts +39 -7
  3. package/loader-hook.mjs +52 -1
  4. package/package.json +8 -16
  5. package/packages/datadog-core/src/utils/src/set.js +5 -1
  6. package/packages/datadog-esbuild/index.js +105 -36
  7. package/packages/datadog-esbuild/src/utils.js +198 -0
  8. package/packages/datadog-instrumentations/src/cookie-parser.js +0 -2
  9. package/packages/datadog-instrumentations/src/cucumber.js +2 -2
  10. package/packages/datadog-instrumentations/src/express.js +82 -0
  11. package/packages/datadog-instrumentations/src/helpers/router-helper.js +238 -0
  12. package/packages/datadog-instrumentations/src/jest.js +2 -1
  13. package/packages/datadog-instrumentations/src/mariadb.js +9 -7
  14. package/packages/datadog-instrumentations/src/playwright.js +226 -93
  15. package/packages/datadog-instrumentations/src/router.js +63 -6
  16. package/packages/datadog-instrumentations/src/vitest.js +44 -12
  17. package/packages/datadog-instrumentations/src/ws.js +3 -3
  18. package/packages/datadog-plugin-aws-sdk/src/base.js +0 -1
  19. package/packages/datadog-plugin-express/src/code_origin.js +2 -0
  20. package/packages/datadog-plugin-playwright/src/index.js +74 -31
  21. package/packages/datadog-plugin-ws/src/close.js +1 -1
  22. package/packages/datadog-shimmer/src/shimmer.js +2 -0
  23. package/packages/dd-trace/src/aiguard/sdk.js +25 -3
  24. package/packages/dd-trace/src/aiguard/tags.js +4 -1
  25. package/packages/dd-trace/src/config-helper.js +4 -1
  26. package/packages/dd-trace/src/config.js +599 -592
  27. package/packages/dd-trace/src/config_defaults.js +14 -12
  28. package/packages/dd-trace/src/plugins/util/ci.js +3 -2
  29. package/packages/dd-trace/src/plugins/util/stacktrace.js +16 -1
  30. package/packages/dd-trace/src/proxy.js +1 -1
  31. package/packages/dd-trace/src/supported-configurations.json +1 -0
  32. package/packages/dd-trace/src/telemetry/endpoints.js +27 -1
  33. package/packages/dd-trace/src/telemetry/index.js +16 -13
  34. package/packages/dd-trace/src/telemetry/logs/log-collector.js +5 -3
  35. package/register.js +1 -11
  36. package/scripts/preinstall.js +3 -1
  37. package/version.js +2 -1
@@ -14,6 +14,7 @@ require,@opentelemetry/resources,Apache license 2.0,Copyright OpenTelemetry Auth
14
14
  require,@isaacs/ttlcache,ISC,Copyright (c) 2022-2023 - Isaac Z. Schlueter and Contributors
15
15
  require,crypto-randomuuid,MIT,Copyright 2021 Node.js Foundation and contributors
16
16
  require,dc-polyfill,MIT,Copyright 2023 Datadog Inc.
17
+ require,escape-string-regexp,MIT,Copyright Sindre Sorhus
17
18
  require,ignore,MIT,Copyright 2013 Kael Zhang and contributors
18
19
  require,import-in-the-middle,Apache license 2.0,Copyright 2021 Datadog Inc.
19
20
  require,istanbul-lib-coverage,BSD-3-Clause,Copyright 2012-2015 Yahoo! Inc.
@@ -50,6 +51,7 @@ dev,@stylistic/eslint-plugin,MIT,Copyright OpenJS Foundation and other contribut
50
51
  dev,axios,MIT,Copyright 2014-present Matt Zabriskie
51
52
  dev,benchmark,MIT,Copyright 2010-2016 Mathias Bynens Robert Kieffer John-David Dalton
52
53
  dev,body-parser,MIT,Copyright 2014 Jonathan Ong 2014-2015 Douglas Christopher Wilson
54
+ dev,bun,MIT,Copyright contributors
53
55
  dev,chai,MIT,Copyright 2017 Chai.js Assertion Library
54
56
  dev,eslint,MIT,Copyright JS Foundation and other contributors https://js.foundation
55
57
  dev,eslint-plugin-cypress,MIT,Copyright (c) 2019 Cypress.io
package/index.d.ts CHANGED
@@ -134,6 +134,11 @@ interface Tracer extends opentracing.Tracer {
134
134
 
135
135
  dogstatsd: tracer.DogStatsD;
136
136
 
137
+ /**
138
+ * Data Streams manual checkpointer API.
139
+ */
140
+ dataStreamsCheckpointer: tracer.DataStreamsCheckpointer;
141
+
137
142
  /**
138
143
  * LLM Observability SDK
139
144
  */
@@ -176,6 +181,7 @@ interface Tracer extends opentracing.Tracer {
176
181
  /** @hidden */
177
182
  interface Plugins {
178
183
  "aerospike": tracer.plugins.aerospike;
184
+ "ai": tracer.plugins.ai;
179
185
  "amqp10": tracer.plugins.amqp10;
180
186
  "amqplib": tracer.plugins.amqplib;
181
187
  "anthropic": tracer.plugins.anthropic;
@@ -1026,6 +1032,29 @@ declare namespace tracer {
1026
1032
  flush(): void
1027
1033
  }
1028
1034
 
1035
+ /**
1036
+ * Manual Data Streams Monitoring checkpointer API.
1037
+ */
1038
+ export interface DataStreamsCheckpointer {
1039
+ /**
1040
+ * Sets a produce checkpoint and injects the DSM context into the provided carrier.
1041
+ * @param type The streaming technology (e.g., kafka, kinesis, sns).
1042
+ * @param target The target of data (topic, exchange, stream name).
1043
+ * @param carrier The carrier object to inject DSM context into.
1044
+ */
1045
+ setProduceCheckpoint (type: string, target: string, carrier: any): void;
1046
+
1047
+ /**
1048
+ * Sets a consume checkpoint and extracts DSM context from the provided carrier.
1049
+ * @param type The streaming technology (e.g., kafka, kinesis, sns).
1050
+ * @param source The source of data (topic, exchange, stream name).
1051
+ * @param carrier The carrier object to extract DSM context from.
1052
+ * @param manualCheckpoint Whether this checkpoint was manually set. Defaults to true.
1053
+ * @returns The DSM context associated with the current pathway.
1054
+ */
1055
+ setConsumeCheckpoint (type: string, source: string, carrier: any, manualCheckpoint?: boolean): any;
1056
+ }
1057
+
1029
1058
  export interface EventTrackingV2 {
1030
1059
  /**
1031
1060
  * Links a successful login event to the current trace. Will link the passed user to the current trace with Appsec.setUser() internally.
@@ -1614,6 +1643,12 @@ declare namespace tracer {
1614
1643
  */
1615
1644
  interface aerospike extends Instrumentation {}
1616
1645
 
1646
+ /**
1647
+ * This plugin automatically instruments the
1648
+ * [Vercel AI SDK](https://ai-sdk.dev/docs/introduction) module.
1649
+ */
1650
+ interface ai extends Instrumentation {}
1651
+
1617
1652
  /**
1618
1653
  * This plugin automatically instruments the
1619
1654
  * [amqp10](https://github.com/noodlefrenzy/node-amqp10) module.
@@ -1670,12 +1705,6 @@ declare namespace tracer {
1670
1705
  * [aws-sdk](https://github.com/aws/aws-sdk-js) module.
1671
1706
  */
1672
1707
  interface aws_sdk extends Instrumentation {
1673
- /**
1674
- * Whether to add a suffix to the service name so that each AWS service has its own service name.
1675
- * @default true
1676
- */
1677
- splitByAwsService?: boolean;
1678
-
1679
1708
  /**
1680
1709
  * Whether to inject all messages during batch AWS SQS, Kinesis, and SNS send operations. Normal
1681
1710
  * behavior is to inject the first message in batch send operations.
@@ -2793,7 +2822,10 @@ declare namespace tracer {
2793
2822
  redactionValuePattern?: string,
2794
2823
 
2795
2824
  /**
2796
- * Allows to enable security controls.
2825
+ * Allows to enable security controls. This option is not supported when
2826
+ * using ESM.
2827
+ * @deprecated Please use the DD_IAST_SECURITY_CONTROLS_CONFIGURATION
2828
+ * environment variable instead.
2797
2829
  */
2798
2830
  securityControlsConfiguration?: string,
2799
2831
 
package/loader-hook.mjs CHANGED
@@ -1 +1,52 @@
1
- export * from 'import-in-the-middle/hook.mjs'
1
+ import regexpEscape from 'escape-string-regexp'
2
+ import * as iitm from 'import-in-the-middle/hook.mjs'
3
+ import hooks from './packages/datadog-instrumentations/src/helpers/hooks.js'
4
+ import configHelper from './packages/dd-trace/src/config-helper.js'
5
+
6
+ // For some reason `getEnvironmentVariable` is not otherwise available to ESM.
7
+ const env = configHelper.getEnvironmentVariable
8
+
9
+ function initialize (data = {}) {
10
+ data.include ??= []
11
+ data.exclude ??= []
12
+
13
+ addInstrumentations(data)
14
+ addSecurityControls(data)
15
+ addExclusions(data)
16
+
17
+ return iitm.initialize(data)
18
+ }
19
+
20
+ function addInstrumentations (data) {
21
+ const instrumentations = Object.keys(hooks)
22
+
23
+ for (const moduleName of instrumentations) {
24
+ data.include.push(new RegExp(`node_modules/${moduleName}/(?!node_modules).+`), moduleName)
25
+ }
26
+ }
27
+
28
+ function addSecurityControls (data) {
29
+ const securityControls = (env('DD_IAST_SECURITY_CONTROLS_CONFIGURATION') || '')
30
+ .split(';')
31
+ .map(sc => sc.trim().split(':')[2])
32
+ .filter(Boolean)
33
+ .map(sc => sc.trim())
34
+
35
+ for (const subpath of securityControls) {
36
+ data.include.push(new RegExp(regexpEscape(subpath)))
37
+ }
38
+ }
39
+
40
+ function addExclusions (data) {
41
+ data.exclude.push(
42
+ /middle/,
43
+ /langsmith/,
44
+ /openai\/_shims/,
45
+ /openai\/resources\/chat\/completions\/messages/,
46
+ /openai\/agents-core\/dist\/shims/,
47
+ /@anthropic-ai\/sdk\/_shims/
48
+ )
49
+ }
50
+
51
+ export { initialize }
52
+ export { load, getFormat, resolve, getSource } from 'import-in-the-middle/hook.mjs'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dd-trace",
3
- "version": "5.73.0",
3
+ "version": "5.75.0",
4
4
  "description": "Datadog APM tracing client for JavaScript",
5
5
  "main": "index.js",
6
6
  "typings": "index.d.ts",
@@ -32,6 +32,8 @@
32
32
  "test:trace:core:ci": "npm run test:trace:core -- --coverage --nyc-arg=--include=\"packages/dd-trace/src/**/*.js\"",
33
33
  "test:trace:guardrails": "mocha -r \"packages/dd-trace/test/setup/mocha.js\" \"packages/dd-trace/test/guardrails/**/*.spec.js\"",
34
34
  "test:trace:guardrails:ci": "nyc --no-clean --include \"packages/dd-trace/src/guardrails/**/*.js\" -- npm run test:trace:guardrails",
35
+ "test:esbuild": "mocha -r \"packages/dd-trace/test/setup/mocha.js\" \"packages/datadog-esbuild/test/**/*.spec.js\"",
36
+ "test:esbuild:ci": "nyc --no-clean --include \"packages/datadog-esbuild/test/**/*.js\" -- npm run test:esbuild",
35
37
  "test:instrumentations": "mocha -r 'packages/dd-trace/test/setup/mocha.js' \"packages/datadog-instrumentations/test/@($(echo $PLUGINS)).spec.js\"",
36
38
  "test:instrumentations:ci": "yarn services && nyc --no-clean --include \"packages/datadog-instrumentations/src/@($(echo $PLUGINS)).js\" --include \"packages/datadog-instrumentations/src/@($(echo $PLUGINS))/**/*.js\" -- npm run test:instrumentations",
37
39
  "test:instrumentations:misc": "mocha -r 'packages/dd-trace/test/setup/mocha.js' 'packages/datadog-instrumentations/test/*/**/*.spec.js'",
@@ -134,6 +136,7 @@
134
136
  "@opentelemetry/resources": ">=1.0.0 <1.10.0",
135
137
  "crypto-randomuuid": "^1.0.0",
136
138
  "dc-polyfill": "^0.1.10",
139
+ "escape-string-regexp": "^5.0.0",
137
140
  "ignore": "^7.0.5",
138
141
  "import-in-the-middle": "^1.14.2",
139
142
  "istanbul-lib-coverage": "^3.2.2",
@@ -156,25 +159,13 @@
156
159
  "tlhunter-sorted-set": "^0.1.0",
157
160
  "ttl-set": "^1.0.0"
158
161
  },
159
- "peerDependencies": {
160
- "@openfeature/core": "^1.9.0",
161
- "@openfeature/server-sdk": "~1.19.0"
162
- },
163
- "peerDependenciesMeta": {
164
- "@openfeature/core": {
165
- "optional": true
166
- },
167
- "@openfeature/server-sdk": {
168
- "optional": true
169
- }
170
- },
171
162
  "devDependencies": {
172
163
  "@babel/helpers": "^7.27.6",
173
164
  "@eslint/eslintrc": "^3.3.1",
174
165
  "@eslint/js": "^9.29.0",
175
166
  "@msgpack/msgpack": "^3.1.2",
176
- "@openfeature/core": "^1.8.1",
177
- "@openfeature/server-sdk": "~1.19.0",
167
+ "@openfeature/core": "^1.9.0",
168
+ "@openfeature/server-sdk": "^1.20.0",
178
169
  "@stylistic/eslint-plugin": "^5.0.0",
179
170
  "@types/chai": "^4.3.16",
180
171
  "@types/mocha": "^10.0.10",
@@ -184,6 +175,7 @@
184
175
  "axios": "^1.12.2",
185
176
  "benchmark": "^2.1.4",
186
177
  "body-parser": "^2.2.0",
178
+ "bun": "1.3.1",
187
179
  "chai": "^4.5.0",
188
180
  "eslint": "^9.29.0",
189
181
  "eslint-plugin-cypress": "^5.1.0",
@@ -211,7 +203,7 @@
211
203
  "tap": "^16.3.10",
212
204
  "tiktoken": "^1.0.21",
213
205
  "typescript": "^5.9.2",
214
- "workerpool": "^9.2.0",
206
+ "workerpool": "^10.0.0",
215
207
  "yaml": "^2.8.0",
216
208
  "yarn-deduplicate": "^6.0.2"
217
209
  }
@@ -5,7 +5,11 @@ module.exports = function set (object, path, value) {
5
5
  while (true) {
6
6
  const nextIndex = path.indexOf('.', index + 1)
7
7
  if (nextIndex === -1) {
8
- object[path.slice(index + 1)] = value
8
+ if (index === -1) {
9
+ object[path] = value
10
+ } else {
11
+ object[path.slice(index + 1)] = value
12
+ }
9
13
  return
10
14
  }
11
15
  object = object[path.slice(index + 1, nextIndex)] ??= {}
@@ -8,6 +8,12 @@ const extractPackageAndModulePath = require(
8
8
  '../datadog-instrumentations/src/helpers/extract-package-and-module-path.js'
9
9
  )
10
10
 
11
+ const { pathToFileURL, fileURLToPath } = require('url')
12
+ const { processModule, isESMFile } = require('./src/utils.js')
13
+
14
+ const ESM_INTERCEPTED_SUFFIX = '._dd_esbuild_intercepted'
15
+ const INTERNAL_ESM_INTERCEPTED_PREFIX = '/_dd_esm_internal_/'
16
+
11
17
  let rewriter
12
18
 
13
19
  for (const hook of Object.values(hooks)) {
@@ -30,7 +36,6 @@ for (const instrumentation of Object.values(instrumentations)) {
30
36
  }
31
37
  }
32
38
 
33
- const INSTRUMENTED = Object.keys(instrumentations)
34
39
  const RAW_BUILTINS = require('module').builtinModules
35
40
  const CHANNEL = 'dd-trace:bundler:load'
36
41
  const path = require('path')
@@ -47,14 +52,6 @@ for (const builtin of RAW_BUILTINS) {
47
52
  const DEBUG = !!process.env.DD_TRACE_DEBUG
48
53
  const DD_IAST_ENABLED = process.env.DD_IAST_ENABLED?.toLowerCase() === 'true' || process.env.DD_IAST_ENABLED === '1'
49
54
 
50
- // We don't want to handle any built-in packages
51
- // Those packages will still be handled via RITM
52
- // Attempting to instrument them would fail as they have no package.json file
53
- for (const pkg of INSTRUMENTED) {
54
- if (builtins.has(pkg) || pkg.startsWith('node:')) continue
55
- modulesOfInterest.add(pkg)
56
- }
57
-
58
55
  module.exports.name = 'datadog-esbuild'
59
56
 
60
57
  function isESMBuild (build) {
@@ -117,13 +114,15 @@ ${build.initialOptions.banner.js}`
117
114
  }
118
115
 
119
116
  try {
117
+ // eslint-disable-next-line n/no-unpublished-require
120
118
  require.resolve('@openfeature/core')
121
119
  } catch (error) {
122
120
  build.initialOptions.external ??= []
123
121
  build.initialOptions.external.push('@openfeature/core')
124
122
  }
125
123
 
126
- if (isESMBuild(build)) {
124
+ const esmBuild = isESMBuild(build)
125
+ if (esmBuild) {
127
126
  if (!build.initialOptions.banner.js.includes('import { createRequire as $dd_createRequire } from \'module\'')) {
128
127
  build.initialOptions.banner.js = `import { createRequire as $dd_createRequire } from 'module';
129
128
  import { fileURLToPath as $dd_fileURLToPath } from 'url';
@@ -157,6 +156,9 @@ ${build.initialOptions.banner.js}`
157
156
  console.warn('Warning: No git metadata available - skipping injection')
158
157
  }
159
158
 
159
+ // first time is intercepted, proxy should be created, next time the original should be loaded
160
+ const interceptedESMModules = new Set()
161
+
160
162
  build.onResolve({ filter: /.*/ }, args => {
161
163
  if (externalModules.has(args.path)) {
162
164
  // Internal Node.js packages will still be instrumented via require()
@@ -175,7 +177,7 @@ ${build.initialOptions.banner.js}`
175
177
 
176
178
  let fullPathToModule
177
179
  try {
178
- fullPathToModule = dotFriendlyResolve(args.path, args.resolveDir)
180
+ fullPathToModule = dotFriendlyResolve(args.path, args.resolveDir, args.kind === 'import-statement')
179
181
  } catch (err) {
180
182
  if (DEBUG) {
181
183
  console.warn(`Warning: Unable to find "${args.path}".` +
@@ -205,8 +207,25 @@ ${build.initialOptions.banner.js}`
205
207
  if (args.namespace === 'file' && (
206
208
  modulesOfInterest.has(args.path) || modulesOfInterest.has(`${extracted.pkg}/${extracted.path}`))
207
209
  ) {
210
+ // Internal module like http/fs is imported and the build output is ESM
211
+ if (internal && args.kind === 'import-statement' && esmBuild && !interceptedESMModules.has(fullPathToModule)) {
212
+ fullPathToModule = `${INTERNAL_ESM_INTERCEPTED_PREFIX}${fullPathToModule}${ESM_INTERCEPTED_SUFFIX}`
213
+
214
+ return {
215
+ path: fullPathToModule,
216
+ pluginData: {
217
+ pkg: extracted?.pkg,
218
+ path: extracted?.path,
219
+ full: fullPathToModule,
220
+ raw: args.path,
221
+ pkgOfInterest: true,
222
+ kind: args.kind,
223
+ internal,
224
+ isESM: true
225
+ }
226
+ }
227
+ }
208
228
  // The file namespace is used when requiring files from disk in userland
209
-
210
229
  let pathToPackageJson
211
230
  try {
212
231
  // we can't use require.resolve('pkg/package.json') as ESM modules don't make the file available
@@ -228,6 +247,11 @@ ${build.initialOptions.banner.js}`
228
247
 
229
248
  const packageJson = JSON.parse(fs.readFileSync(pathToPackageJson).toString())
230
249
 
250
+ const isESM = isESMFile(fullPathToModule, pathToPackageJson, packageJson)
251
+ if (isESM && !interceptedESMModules.has(fullPathToModule)) {
252
+ fullPathToModule += ESM_INTERCEPTED_SUFFIX
253
+ }
254
+
231
255
  if (DEBUG) console.log(`RESOLVE: ${args.path}@${packageJson.version}`)
232
256
 
233
257
  // https://esbuild.github.io/plugins/#on-resolve-arguments
@@ -240,13 +264,15 @@ ${build.initialOptions.banner.js}`
240
264
  full: fullPathToModule,
241
265
  raw: args.path,
242
266
  pkgOfInterest: true,
243
- internal
267
+ kind: args.kind,
268
+ internal,
269
+ isESM
244
270
  }
245
271
  }
246
272
  }
247
273
  })
248
274
 
249
- build.onLoad({ filter: /.*/ }, args => {
275
+ build.onLoad({ filter: /.*/ }, async args => {
250
276
  if (args.pluginData?.pkgOfInterest) {
251
277
  const data = args.pluginData
252
278
 
@@ -257,26 +283,63 @@ ${build.initialOptions.banner.js}`
257
283
  : data.pkg
258
284
 
259
285
  // Read the content of the module file of interest
260
- const fileCode = fs.readFileSync(args.path, 'utf8')
286
+ let contents
287
+
288
+ if (data.isESM) {
289
+ if (args.path.endsWith(ESM_INTERCEPTED_SUFFIX)) {
290
+ args.path = args.path.slice(0, -1 * ESM_INTERCEPTED_SUFFIX.length)
291
+
292
+ if (data.internal) {
293
+ args.path = args.path.slice(INTERNAL_ESM_INTERCEPTED_PREFIX.length)
294
+ }
295
+
296
+ interceptedESMModules.add(args.path)
297
+
298
+ const setters = await processModule({
299
+ path: args.path,
300
+ internal: data.internal,
301
+ context: { format: 'module' }
302
+ })
303
+
304
+ const iitmPath = require.resolve('import-in-the-middle/lib/register.js')
305
+ const toRegister = data.internal ? args.path : pathToFileURL(args.path)
306
+ // Mimic a Module object (https://tc39.es/ecma262/#sec-module-namespace-objects).
307
+ contents = `
308
+ import { register } from ${JSON.stringify(iitmPath)};
309
+ import * as namespace from ${JSON.stringify(args.path)};
310
+ const _ = Object.create(null, { [Symbol.toStringTag]: { value: 'Module' } });
311
+ const set = {};
312
+ const get = {};
313
+
314
+ ${Array.from(setters.values()).join(';\n')};
315
+
316
+ register(${JSON.stringify(toRegister)}, _, set, get, ${JSON.stringify(data.raw)});
317
+ `
318
+ } else {
319
+ contents = fs.readFileSync(args.path, 'utf8')
320
+ }
321
+ } else {
322
+ const fileCode = fs.readFileSync(args.path, 'utf8')
323
+ contents = `
324
+ (function() {
325
+ ${fileCode}
326
+ })(...arguments);
327
+ {
328
+ const dc = require('dc-polyfill');
329
+ const ch = dc.channel('${CHANNEL}');
330
+ const mod = module.exports
331
+ const payload = {
332
+ module: mod,
333
+ version: '${data.version}',
334
+ package: '${data.pkg}',
335
+ path: '${pkgPath}'
336
+ };
337
+ ch.publish(payload);
338
+ module.exports = payload.module;
339
+ }
340
+ `
341
+ }
261
342
 
262
- const contents = `
263
- (function() {
264
- ${fileCode}
265
- })(...arguments);
266
- {
267
- const dc = require('dc-polyfill');
268
- const ch = dc.channel('${CHANNEL}');
269
- const mod = module.exports
270
- const payload = {
271
- module: mod,
272
- version: '${data.version}',
273
- package: '${data.pkg}',
274
- path: '${pkgPath}'
275
- };
276
- ch.publish(payload);
277
- module.exports = payload.module;
278
- }
279
- `
280
343
  // https://esbuild.github.io/plugins/#on-load-results
281
344
  return {
282
345
  contents,
@@ -284,7 +347,6 @@ ${build.initialOptions.banner.js}`
284
347
  resolveDir: path.dirname(args.path)
285
348
  }
286
349
  }
287
-
288
350
  if (DD_IAST_ENABLED && args.pluginData?.applicationFile) {
289
351
  const ext = path.extname(args.path).toLowerCase()
290
352
  const isJs = /^\.(js|mjs|cjs)$/.test(ext)
@@ -303,12 +365,19 @@ ${build.initialOptions.banner.js}`
303
365
  }
304
366
 
305
367
  // @see https://github.com/nodejs/node/issues/47000
306
- function dotFriendlyResolve (path, directory) {
368
+ function dotFriendlyResolve (path, directory, usesImportStatement) {
307
369
  if (path === '.') {
308
370
  path = './'
309
371
  } else if (path === '..') {
310
372
  path = '../'
311
373
  }
374
+ let conditions
375
+ if (usesImportStatement) {
376
+ conditions = new Set(['import', 'node'])
377
+ }
312
378
 
313
- return require.resolve(path, { paths: [directory] })
379
+ if (path.startsWith('file://')) {
380
+ path = fileURLToPath(path)
381
+ }
382
+ return require.resolve(path, { paths: [directory], conditions })
314
383
  }
@@ -0,0 +1,198 @@
1
+ 'use strict'
2
+
3
+ // The content of this file is copied from the `import-in-the-middle` package with minor modifications (https://www.npmjs.com/package/import-in-the-middle)
4
+ const { pathToFileURL, fileURLToPath } = require('node:url')
5
+ const fs = require('node:fs')
6
+ const path = require('node:path')
7
+ const { NODE_MAJOR, NODE_MINOR } = require('../../../version.js')
8
+
9
+ const getExportsImporting = (url) => import(url).then(Object.keys)
10
+ const getExports = NODE_MAJOR >= 20 || (NODE_MAJOR === 18 && NODE_MINOR >= 19)
11
+ ? require('import-in-the-middle/lib/get-exports.js')
12
+ : getExportsImporting
13
+
14
+ function isStarExportLine (line) {
15
+ return /^\* from /.test(line)
16
+ }
17
+
18
+ function isBareSpecifier (specifier) {
19
+ // Relative and absolute paths are not bare specifiers.
20
+ if (
21
+ specifier.startsWith('.') ||
22
+ specifier.startsWith('/')) {
23
+ return false
24
+ }
25
+
26
+ // Valid URLs are not bare specifiers. (file:, http:, node:, etc.)
27
+
28
+ if (URL.hasOwnProperty('canParse')) {
29
+ // eslint-disable-next-line n/no-unsupported-features/node-builtins
30
+ return !URL.canParse(specifier)
31
+ }
32
+
33
+ try {
34
+ // eslint-disable-next-line no-new
35
+ new URL(specifier)
36
+ return false
37
+ } catch {
38
+ return true
39
+ }
40
+ }
41
+
42
+ function resolve (specifier, context) {
43
+ // This comes from an import, that is why import makes preference
44
+ const conditions = ['import']
45
+
46
+ if (specifier.startsWith('file://')) {
47
+ specifier = fileURLToPath(specifier)
48
+ }
49
+
50
+ const resolved = require.resolve(specifier, { conditions, paths: [fileURLToPath(context.parentURL)] })
51
+
52
+ return {
53
+ url: pathToFileURL(resolved),
54
+ format: isESMFile(resolved) ? 'module' : 'commonjs'
55
+ }
56
+ }
57
+
58
+ function getSource (url, { format }) {
59
+ return {
60
+ source: fs.readFileSync(fileURLToPath(url), 'utf8'),
61
+ format
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Generates the pieces of code for the proxy module before the path
67
+ *
68
+ * @param {Object} moduleData { path, internal, context, excludeDefault }
69
+ * @returns {Promise<Map>}
70
+ */
71
+ async function processModule ({ path, internal, context, excludeDefault }) {
72
+ let exportNames, srcUrl
73
+ if (internal) {
74
+ // we can not read and parse of internal modules
75
+ exportNames = await getExportsImporting(path)
76
+ } else {
77
+ srcUrl = pathToFileURL(path)
78
+ exportNames = await getExports(srcUrl, context, getSource)
79
+ }
80
+
81
+ const starExports = new Set()
82
+ const setters = new Map()
83
+
84
+ const addSetter = (name, setter, isStarExport = false) => {
85
+ if (setters.has(name)) {
86
+ if (isStarExport) {
87
+ // If there's already a matching star export, delete it
88
+ if (starExports.has(name)) {
89
+ setters.delete(name)
90
+ }
91
+ // and return so this is excluded
92
+ return
93
+ }
94
+
95
+ // if we already have this export but it is from a * export, overwrite it
96
+ if (starExports.has(name)) {
97
+ starExports.delete(name)
98
+ setters.set(name, setter)
99
+ }
100
+ } else {
101
+ // Store export * exports so we know they can be overridden by explicit
102
+ // named exports
103
+ if (isStarExport) {
104
+ starExports.add(name)
105
+ }
106
+
107
+ setters.set(name, setter)
108
+ }
109
+ }
110
+
111
+ for (const n of exportNames) {
112
+ if (n === 'default' && excludeDefault) continue
113
+
114
+ if (isStarExportLine(n) === true) {
115
+ // export * from 'wherever'
116
+ const [, modFile] = n.split('* from ')
117
+
118
+ // Relative paths need to be resolved relative to the parent module
119
+ const newSpecifier = isBareSpecifier(modFile) ? modFile : new URL(modFile, srcUrl).href
120
+ // We need to call `parentResolve` to resolve bare specifiers to a full
121
+ // URL. We also need to call `parentResolve` for all sub-modules to get
122
+ // the `format`. We can't rely on the parents `format` to know if this
123
+ // sub-module is ESM or CJS!
124
+
125
+ const result = resolve(newSpecifier, { parentURL: srcUrl })
126
+
127
+ // eslint-disable-next-line no-await-in-loop
128
+ const subSetters = await processModule({
129
+ path: fileURLToPath(result.url),
130
+ context: { ...context, format: result.format },
131
+ excludeDefault: true
132
+ })
133
+
134
+ for (const [name, setter] of subSetters.entries()) {
135
+ addSetter(name, setter, true)
136
+ }
137
+ } else {
138
+ const variableName = `$${n.replaceAll(/[^a-zA-Z0-9_$]/g, '_')}`
139
+ const objectKey = JSON.stringify(n)
140
+ const reExportedName = n === 'default' ? n : objectKey
141
+
142
+ addSetter(n, `
143
+ let ${variableName}
144
+ try {
145
+ ${variableName} = _[${objectKey}] = namespace[${objectKey}]
146
+ } catch (err) {
147
+ if (!(err instanceof ReferenceError)) throw err
148
+ }
149
+ export { ${variableName} as ${reExportedName} }
150
+ set[${objectKey}] = (v) => {
151
+ ${variableName} = v
152
+ return true
153
+ }
154
+ get[${objectKey}] = () => ${variableName}
155
+ `)
156
+ }
157
+ }
158
+
159
+ return setters
160
+ }
161
+
162
+ /**
163
+ * Determines if a file is a ESM module or CommonJS
164
+ *
165
+ * @param {string} fullPathToModule File to analize
166
+ * @param {string} [modulePackageJsonPath] Path of the package.json
167
+ * @param {Object} [packageJson] The content of the module package.json
168
+ * @returns {boolean}
169
+ */
170
+ function isESMFile (fullPathToModule, modulePackageJsonPath, packageJson = {}) {
171
+ if (fullPathToModule.endsWith('.mjs')) return true
172
+ if (fullPathToModule.endsWith('.cjs')) return false
173
+
174
+ const pathParts = fullPathToModule.split(path.sep)
175
+ do {
176
+ pathParts.pop()
177
+
178
+ const packageJsonPath = [...pathParts, 'package.json'].join(path.sep)
179
+ if (packageJsonPath === modulePackageJsonPath) {
180
+ return packageJson.type === 'module'
181
+ }
182
+
183
+ try {
184
+ const packageJsonContent = fs.readFileSync(packageJsonPath).toString()
185
+ const packageJson = JSON.parse(packageJsonContent)
186
+ return packageJson.type === 'module'
187
+ } catch {
188
+ // file does not exit, continue
189
+ }
190
+ } while (pathParts.length > 0)
191
+
192
+ return packageJson.type === 'module'
193
+ }
194
+
195
+ module.exports = {
196
+ processModule,
197
+ isESMFile
198
+ }
@@ -25,8 +25,6 @@ addHook({
25
25
  name: 'cookie-parser',
26
26
  versions: ['>=1.0.0']
27
27
  }, cookieParser => {
28
- // This prevents the non default export from entering the wrapping process
29
- if (cookieParser.default) return cookieParser
30
28
  return shimmer.wrapFunction(cookieParser, cookieParser => function () {
31
29
  const cookieMiddleware = cookieParser.apply(this, arguments)
32
30