dd-trace 5.0.0-pre-cd5fbdb → 5.0.0-pre-4c05491

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 (43) hide show
  1. package/ext/tags.d.ts +1 -0
  2. package/ext/tags.js +1 -0
  3. package/index.d.ts +4 -4
  4. package/package.json +6 -5
  5. package/packages/datadog-instrumentations/src/body-parser.js +2 -2
  6. package/packages/datadog-instrumentations/src/cookie-parser.js +37 -0
  7. package/packages/datadog-instrumentations/src/express.js +1 -1
  8. package/packages/datadog-instrumentations/src/graphql.js +5 -4
  9. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
  10. package/packages/datadog-instrumentations/src/http/server.js +1 -1
  11. package/packages/datadog-instrumentations/src/mysql.js +39 -1
  12. package/packages/datadog-instrumentations/src/next.js +22 -80
  13. package/packages/datadog-instrumentations/src/pg.js +14 -15
  14. package/packages/datadog-instrumentations/src/playwright.js +15 -3
  15. package/packages/datadog-plugin-graphql/src/resolve.js +27 -2
  16. package/packages/datadog-plugin-next/src/index.js +14 -5
  17. package/packages/dd-trace/src/appsec/addresses.js +1 -0
  18. package/packages/dd-trace/src/appsec/channels.js +2 -0
  19. package/packages/dd-trace/src/appsec/iast/analyzers/ldap-injection-analyzer.js +7 -0
  20. package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +28 -32
  21. package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +19 -1
  22. package/packages/dd-trace/src/appsec/iast/path-line.js +1 -0
  23. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +48 -5
  24. package/packages/dd-trace/src/appsec/index.js +42 -7
  25. package/packages/dd-trace/src/appsec/recommended.json +655 -31
  26. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +2 -1
  27. package/packages/dd-trace/src/appsec/remote_config/index.js +2 -0
  28. package/packages/dd-trace/src/appsec/reporter.js +26 -0
  29. package/packages/dd-trace/src/appsec/telemetry.js +132 -0
  30. package/packages/dd-trace/src/appsec/waf/index.js +1 -1
  31. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +13 -5
  32. package/packages/dd-trace/src/appsec/waf/waf_manager.js +12 -14
  33. package/packages/dd-trace/src/datastreams/processor.js +6 -2
  34. package/packages/dd-trace/src/dogstatsd.js +45 -5
  35. package/packages/dd-trace/src/format.js +6 -1
  36. package/packages/dd-trace/src/plugins/index.js +1 -0
  37. package/packages/dd-trace/src/plugins/tracing.js +1 -1
  38. package/packages/dd-trace/src/profiling/profilers/wall.js +14 -6
  39. package/packages/dd-trace/src/proxy.js +1 -11
  40. package/packages/dd-trace/src/runtime_metrics.js +1 -32
  41. package/packages/dd-trace/src/telemetry/index.js +10 -1
  42. package/packages/dd-trace/src/telemetry/metrics.js +0 -5
  43. package/packages/dd-trace/src/util.js +1 -1
package/ext/tags.d.ts CHANGED
@@ -9,6 +9,7 @@ declare const tags: {
9
9
  MANUAL_KEEP: 'manual.keep'
10
10
  MANUAL_DROP: 'manual.drop'
11
11
  MEASURED: '_dd.measured'
12
+ BASE_SERVICE: '_dd.base_service'
12
13
  HTTP_URL: 'http.url'
13
14
  HTTP_METHOD: 'http.method'
14
15
  HTTP_STATUS_CODE: 'http.status_code'
package/ext/tags.js CHANGED
@@ -12,6 +12,7 @@ const tags = {
12
12
  MANUAL_KEEP: 'manual.keep',
13
13
  MANUAL_DROP: 'manual.drop',
14
14
  MEASURED: '_dd.measured',
15
+ BASE_SERVICE: '_dd.base_service',
15
16
 
16
17
  // HTTP
17
18
  HTTP_URL: 'http.url',
package/index.d.ts CHANGED
@@ -649,7 +649,7 @@ export declare interface DogStatsD {
649
649
  * Increments a metric by the specified value, optionally specifying tags.
650
650
  * @param {string} stat The dot-separated metric name.
651
651
  * @param {number} value The amount to increment the stat by.
652
- * @param {[tag:string]:string|number} tags Tags to pass along, such as `[ 'foo:bar' ]`. Values are combined with config.tags.
652
+ * @param {[tag:string]:string|number} tags Tags to pass along, such as `{ foo: 'bar' }`. Values are combined with config.tags.
653
653
  */
654
654
  increment(stat: string, value?: number, tags?: { [tag: string]: string|number }): void
655
655
 
@@ -657,7 +657,7 @@ export declare interface DogStatsD {
657
657
  * Decrements a metric by the specified value, optionally specifying tags.
658
658
  * @param {string} stat The dot-separated metric name.
659
659
  * @param {number} value The amount to decrement the stat by.
660
- * @param {[tag:string]:string|number} tags Tags to pass along, such as `[ 'foo:bar' ]`. Values are combined with config.tags.
660
+ * @param {[tag:string]:string|number} tags Tags to pass along, such as `{ foo: 'bar' }`. Values are combined with config.tags.
661
661
  */
662
662
  decrement(stat: string, value?: number, tags?: { [tag: string]: string|number }): void
663
663
 
@@ -665,7 +665,7 @@ export declare interface DogStatsD {
665
665
  * Sets a distribution value, optionally specifying tags.
666
666
  * @param {string} stat The dot-separated metric name.
667
667
  * @param {number} value The amount to increment the stat by.
668
- * @param {[tag:string]:string|number} tags Tags to pass along, such as `[ 'foo:bar' ]`. Values are combined with config.tags.
668
+ * @param {[tag:string]:string|number} tags Tags to pass along, such as `{ foo: 'bar' }`. Values are combined with config.tags.
669
669
  */
670
670
  distribution(stat: string, value?: number, tags?: { [tag: string]: string|number }): void
671
671
 
@@ -673,7 +673,7 @@ export declare interface DogStatsD {
673
673
  * Sets a gauge value, optionally specifying tags.
674
674
  * @param {string} stat The dot-separated metric name.
675
675
  * @param {number} value The amount to increment the stat by.
676
- * @param {[tag:string]:string|number} tags Tags to pass along, such as `[ 'foo:bar' ]`. Values are combined with config.tags.
676
+ * @param {[tag:string]:string|number} tags Tags to pass along, such as `{ foo: 'bar' }`. Values are combined with config.tags.
677
677
  */
678
678
  gauge(stat: string, value?: number, tags?: { [tag: string]: string|number }): void
679
679
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dd-trace",
3
- "version": "5.0.0-pre-cd5fbdb",
3
+ "version": "5.0.0-pre-4c05491",
4
4
  "description": "Datadog APM tracing client for JavaScript",
5
5
  "main": "index.js",
6
6
  "typings": "index.d.ts",
@@ -18,7 +18,7 @@
18
18
  "test:appsec": "mocha --colors --exit -r \"packages/dd-trace/test/setup/mocha.js\" --exclude \"packages/dd-trace/test/appsec/**/*.plugin.spec.js\" \"packages/dd-trace/test/appsec/**/*.spec.js\"",
19
19
  "test:appsec:ci": "nyc --no-clean --include \"packages/dd-trace/src/appsec/**/*.js\" --exclude \"packages/dd-trace/test/appsec/**/*.plugin.spec.js\" -- npm run test:appsec",
20
20
  "test:appsec:plugins": "mocha --colors --exit -r \"packages/dd-trace/test/setup/mocha.js\" \"packages/dd-trace/test/appsec/**/*.@($(echo $PLUGINS)).plugin.spec.js\"",
21
- "test:appsec:plugins:ci": "yarn services && nyc --no-clean --include \"packages/dd-trace/test/appsec/**/*.@($(echo $PLUGINS)).plugin.spec.js\" -- npm run test:appsec:plugins",
21
+ "test:appsec:plugins:ci": "yarn services && nyc --no-clean --include \"packages/dd-trace/src/appsec/**/*.js\" -- npm run test:appsec:plugins",
22
22
  "test:trace:core": "tap packages/dd-trace/test/*.spec.js \"packages/dd-trace/test/{ci-visibility,encode,exporters,opentelemetry,opentracing,plugins,service-naming,telemetry}/**/*.spec.js\"",
23
23
  "test:trace:core:ci": "npm run test:trace:core -- --coverage --nyc-arg=--include=\"packages/dd-trace/src/**/*.js\"",
24
24
  "test:instrumentations": "mocha --colors -r 'packages/dd-trace/test/setup/mocha.js' 'packages/datadog-instrumentations/test/**/*.spec.js'",
@@ -38,6 +38,7 @@
38
38
  "test:integration:playwright": "mocha --colors --timeout 30000 \"integration-tests/playwright/*.spec.js\"",
39
39
  "test:integration:serverless": "mocha --colors --timeout 30000 \"integration-tests/serverless/*.spec.js\"",
40
40
  "test:integration:plugins": "mocha --colors --timeout 30000 \"packages/datadog-plugin-*/test/integration-test/*.spec.js\"",
41
+ "test:unit:plugins": "mocha --colors --exit -r \"packages/dd-trace/test/setup/mocha.js\" \"packages/datadog-instrumentations/test/@($(echo $PLUGINS)).spec.js\" \"packages/datadog-plugin-@($(echo $PLUGINS))/test/**/*.spec.js\" --exclude \"packages/datadog-plugin-@($(echo $PLUGINS))/test/integration-test/**/*.spec.js\"",
41
42
  "test:shimmer": "mocha --colors 'packages/datadog-shimmer/test/**/*.spec.js'",
42
43
  "test:shimmer:ci": "nyc --no-clean --include 'packages/datadog-shimmer/src/**/*.js' -- npm run test:shimmer",
43
44
  "leak:core": "node ./scripts/install_plugin_modules && (cd packages/memwatch && yarn) && NODE_PATH=./packages/memwatch/node_modules node --no-warnings ./node_modules/.bin/tape 'packages/dd-trace/test/leak/**/*.js'",
@@ -58,7 +59,7 @@
58
59
  "apm"
59
60
  ],
60
61
  "author": "Datadog Inc. <info@datadoghq.com>",
61
- "license": "BSD-3-Clause",
62
+ "license": "(Apache-2.0 OR BSD-3-Clause)",
62
63
  "bugs": {
63
64
  "url": "https://github.com/DataDog/dd-trace-js/issues"
64
65
  },
@@ -67,8 +68,8 @@
67
68
  "node": ">=16"
68
69
  },
69
70
  "dependencies": {
70
- "@datadog/native-appsec": "^3.2.0",
71
- "@datadog/native-iast-rewriter": "2.0.1",
71
+ "@datadog/native-appsec": "^4.0.0",
72
+ "@datadog/native-iast-rewriter": "2.1.3",
72
73
  "@datadog/native-iast-taint-tracking": "1.5.0",
73
74
  "@datadog/native-metrics": "^2.0.0",
74
75
  "@datadog/pprof": "3.2.0",
@@ -16,7 +16,7 @@ function publishRequestBodyAndNext (req, res, next) {
16
16
  if (abortController.signal.aborted) return
17
17
  }
18
18
 
19
- next.apply(this, arguments)
19
+ return next.apply(this, arguments)
20
20
  }
21
21
  }
22
22
 
@@ -27,6 +27,6 @@ addHook({
27
27
  }, read => {
28
28
  return shimmer.wrap(read, function (req, res, next) {
29
29
  arguments[2] = publishRequestBodyAndNext(req, res, next)
30
- read.apply(this, arguments)
30
+ return read.apply(this, arguments)
31
31
  })
32
32
  })
@@ -0,0 +1,37 @@
1
+ 'use strict'
2
+
3
+ const { AbortController } = require('node-abort-controller') // AbortController is not available in node <15
4
+ const shimmer = require('../../datadog-shimmer')
5
+ const { channel, addHook } = require('./helpers/instrument')
6
+
7
+ const cookieParserReadCh = channel('datadog:cookie-parser:read:finish')
8
+
9
+ function publishRequestCookieAndNext (req, res, next) {
10
+ return function cookieParserWrapper () {
11
+ if (cookieParserReadCh.hasSubscribers && req) {
12
+ const abortController = new AbortController()
13
+
14
+ const mergedCookies = Object.assign({}, req.cookies, req.signedCookies)
15
+
16
+ cookieParserReadCh.publish({ req, res, abortController, cookies: mergedCookies })
17
+
18
+ if (abortController.signal.aborted) return
19
+ }
20
+
21
+ return next.apply(this, arguments)
22
+ }
23
+ }
24
+
25
+ addHook({
26
+ name: 'cookie-parser',
27
+ versions: ['>=1.0.0']
28
+ }, cookieParser => {
29
+ return shimmer.wrap(cookieParser, function () {
30
+ const cookieMiddleware = cookieParser.apply(this, arguments)
31
+
32
+ return shimmer.wrap(cookieMiddleware, function (req, res, next) {
33
+ arguments[2] = publishRequestCookieAndNext(req, res, next)
34
+ return cookieMiddleware.apply(this, arguments)
35
+ })
36
+ })
37
+ })
@@ -39,7 +39,7 @@ function publishQueryParsedAndNext (req, res, next) {
39
39
  if (abortController.signal.aborted) return
40
40
  }
41
41
 
42
- next.apply(this, arguments)
42
+ return next.apply(this, arguments)
43
43
  }
44
44
  }
45
45
 
@@ -188,7 +188,7 @@ function wrapExecute (execute) {
188
188
  executeErrorCh.publish(error)
189
189
  }
190
190
 
191
- finishExecuteCh.publish({ res, args })
191
+ finishExecuteCh.publish({ res, args, context })
192
192
  })
193
193
  })
194
194
  }
@@ -205,7 +205,7 @@ function wrapResolve (resolve) {
205
205
 
206
206
  if (!context) return resolve.apply(this, arguments)
207
207
 
208
- const field = assertField(context, info)
208
+ const field = assertField(context, info, args)
209
209
 
210
210
  return callInAsyncScope(resolve, field.asyncResource, this, arguments, (err) => {
211
211
  updateFieldCh.publish({ field, info, err })
@@ -250,7 +250,7 @@ function pathToArray (path) {
250
250
  return flattened.reverse()
251
251
  }
252
252
 
253
- function assertField (context, info) {
253
+ function assertField (context, info, args) {
254
254
  const pathInfo = info && info.path
255
255
 
256
256
  const path = pathToArray(pathInfo)
@@ -272,7 +272,8 @@ function assertField (context, info) {
272
272
  childResource.runInAsyncScope(() => {
273
273
  startResolveCh.publish({
274
274
  info,
275
- context
275
+ context,
276
+ args
276
277
  })
277
278
  })
278
279
 
@@ -30,6 +30,7 @@ module.exports = {
30
30
  'node:child_process': () => require('../child-process'),
31
31
  'connect': () => require('../connect'),
32
32
  'cookie': () => require('../cookie'),
33
+ 'cookie-parser': () => require('../cookie-parser'),
33
34
  'couchbase': () => require('../couchbase'),
34
35
  'crypto': () => require('../crypto'),
35
36
  'cypress': () => require('../cypress'),
@@ -81,6 +82,7 @@ module.exports = {
81
82
  'pg': () => require('../pg'),
82
83
  'pino': () => require('../pino'),
83
84
  'pino-pretty': () => require('../pino'),
85
+ 'playwright': () => require('../playwright'),
84
86
  'promise-js': () => require('../promise-js'),
85
87
  'promise': () => require('../promise'),
86
88
  'q': () => require('../q'),
@@ -29,7 +29,7 @@ addHook({ name: 'http' }, http => {
29
29
 
30
30
  function wrapResponseEmit (emit) {
31
31
  return function (eventName, event) {
32
- if (!startServerCh.hasSubscribers) {
32
+ if (!finishServerCh.hasSubscribers) {
33
33
  return emit.apply(this, arguments)
34
34
  }
35
35
 
@@ -17,7 +17,7 @@ addHook({ name: 'mysql', file: 'lib/Connection.js', versions: ['>=2'] }, Connect
17
17
  return query.apply(this, arguments)
18
18
  }
19
19
 
20
- const sql = arguments[0].sql ? arguments[0].sql : arguments[0]
20
+ const sql = arguments[0].sql || arguments[0]
21
21
  const conf = this.config
22
22
  const payload = { sql, conf }
23
23
 
@@ -66,9 +66,47 @@ addHook({ name: 'mysql', file: 'lib/Connection.js', versions: ['>=2'] }, Connect
66
66
  })
67
67
 
68
68
  addHook({ name: 'mysql', file: 'lib/Pool.js', versions: ['>=2'] }, Pool => {
69
+ const startPoolQueryCh = channel('datadog:mysql:pool:query:start')
70
+ const finishPoolQueryCh = channel('datadog:mysql:pool:query:finish')
71
+
69
72
  shimmer.wrap(Pool.prototype, 'getConnection', getConnection => function (cb) {
70
73
  arguments[0] = AsyncResource.bind(cb)
71
74
  return getConnection.apply(this, arguments)
72
75
  })
76
+
77
+ shimmer.wrap(Pool.prototype, 'query', query => function () {
78
+ if (!startPoolQueryCh.hasSubscribers) {
79
+ return query.apply(this, arguments)
80
+ }
81
+
82
+ const asyncResource = new AsyncResource('bound-anonymous-fn')
83
+
84
+ const sql = arguments[0].sql || arguments[0]
85
+
86
+ return asyncResource.runInAsyncScope(() => {
87
+ startPoolQueryCh.publish({ sql })
88
+
89
+ const finish = asyncResource.bind(function () {
90
+ finishPoolQueryCh.publish()
91
+ })
92
+
93
+ const cb = arguments[arguments.length - 1]
94
+ if (typeof cb === 'function') {
95
+ arguments[arguments.length - 1] = shimmer.wrap(cb, function () {
96
+ finish()
97
+ return cb.apply(this, arguments)
98
+ })
99
+ }
100
+
101
+ const retval = query.apply(this, arguments)
102
+
103
+ if (retval && retval.then) {
104
+ retval.then(finish).catch(finish)
105
+ }
106
+
107
+ return retval
108
+ })
109
+ })
110
+
73
111
  return Pool
74
112
  })
@@ -12,7 +12,6 @@ const errorChannel = channel('apm:next:request:error')
12
12
  const pageLoadChannel = channel('apm:next:page:load')
13
13
 
14
14
  const requests = new WeakSet()
15
- const requestToNextjsPagePath = new WeakMap()
16
15
 
17
16
  function wrapHandleRequest (handleRequest) {
18
17
  return function (req, res, pathname, query) {
@@ -29,9 +28,9 @@ function wrapHandleApiRequest (handleApiRequest) {
29
28
  if (!handled) return handled
30
29
 
31
30
  return this.hasPage(pathname).then(pageFound => {
32
- const page = pageFound ? pathname : getPageFromPath(pathname, this.dynamicRoutes)
31
+ const pageData = pageFound ? { page: pathname } : getPageFromPath(pathname, this.dynamicRoutes)
33
32
 
34
- pageLoadChannel.publish({ page })
33
+ pageLoadChannel.publish(pageData)
35
34
 
36
35
  return handled
37
36
  })
@@ -84,15 +83,19 @@ function wrapFindPageComponents (findPageComponents) {
84
83
  const result = findPageComponents.apply(this, arguments)
85
84
 
86
85
  if (result) {
87
- pageLoadChannel.publish({ page: getPagePath(pathname) })
86
+ pageLoadChannel.publish(getPagePath(pathname))
88
87
  }
89
88
 
90
89
  return result
91
90
  }
92
91
  }
93
92
 
94
- function getPagePath (page) {
95
- return typeof page === 'object' ? page.pathname : page
93
+ function getPagePath (maybePage) {
94
+ if (typeof maybePage !== 'object') return { page: maybePage }
95
+
96
+ const isAppPath = maybePage.isAppPath
97
+ const page = maybePage.pathname || maybePage.page
98
+ return { page, isAppPath }
96
99
  }
97
100
 
98
101
  function getPageFromPath (page, dynamicRoutes = []) {
@@ -133,57 +136,16 @@ function instrument (req, res, handler) {
133
136
  })
134
137
  }
135
138
 
136
- function wrapSetupServerWorker (setupServerWorker) {
137
- return function (requestHandler) {
138
- arguments[0] = shimmer.wrap(requestHandler, wrapRequestHandler(requestHandler))
139
- return setupServerWorker.apply(this, arguments)
140
- }
141
- }
142
-
143
- function wrapInitialize (initialize) {
144
- return async function () {
145
- const result = await initialize.apply(this, arguments)
146
- if (Array.isArray(result)) {
147
- const requestHandler = result[0]
148
- result[0] = shimmer.wrap(requestHandler, wrapRequestHandler(requestHandler))
149
- }
150
- return result
151
- }
152
- }
153
-
154
- function wrapRequestHandler (requestHandler) {
155
- return function (req, res) {
156
- return instrument(req, res, async () => {
157
- const result = await requestHandler.apply(this, arguments) // apply here first to get page path association
158
-
159
- const page = requestToNextjsPagePath.get(req)
160
- if (page && pageLoadChannel.hasSubscribers) pageLoadChannel.publish({ page })
139
+ function wrapServeStatic (serveStatic) {
140
+ return function (req, res, path) {
141
+ return instrument(req, res, () => {
142
+ if (pageLoadChannel.hasSubscribers && path) pageLoadChannel.publish({ page: path })
161
143
 
162
- return result
144
+ return serveStatic.apply(this, arguments)
163
145
  })
164
146
  }
165
147
  }
166
148
 
167
- // these two functions make sure we get path groups for routes in standalone,
168
- // as it doesn't route through `next-server`/`base-server`
169
- function wrapGetResolveRoutes (getResolveRoutes) {
170
- return function () {
171
- const result = getResolveRoutes.apply(this, arguments)
172
- return shimmer.wrap(result, wrapResolveRoutes(result))
173
- }
174
- }
175
-
176
- function wrapResolveRoutes (resolveRoutes) {
177
- return async function (req) {
178
- const result = await resolveRoutes.apply(this, arguments)
179
- if (result && result.matchedOutput) {
180
- const path = result.matchedOutput.itemPath
181
- requestToNextjsPagePath.set(req, path)
182
- }
183
- return result
184
- }
185
- }
186
-
187
149
  function finish (ctx, result, err) {
188
150
  if (err) {
189
151
  ctx.error = err
@@ -201,25 +163,21 @@ function finish (ctx, result, err) {
201
163
 
202
164
  addHook({
203
165
  name: 'next',
204
- versions: ['>=13.4.13'],
205
- file: 'dist/server/lib/router-utils/resolve-routes.js'
206
- }, resolveRoutesModule => shimmer.wrap(resolveRoutesModule, 'getResolveRoutes', wrapGetResolveRoutes))
166
+ versions: ['>=11.1'],
167
+ file: 'dist/server/serve-static.js'
168
+ }, serveStatic => shimmer.wrap(serveStatic, 'serveStatic', wrapServeStatic))
207
169
 
208
170
  addHook({
209
171
  name: 'next',
210
- versions: ['13.4.13'],
211
- file: 'dist/server/lib/setup-server-worker.js'
212
- }, setupServerWorker => shimmer.wrap(setupServerWorker, 'initializeServerWorker', wrapSetupServerWorker))
213
-
214
- addHook({
215
- name: 'next',
216
- versions: ['>=13.4.15'],
217
- file: 'dist/server/lib/router-server.js'
218
- }, routerServer => shimmer.wrap(routerServer, 'initialize', wrapInitialize))
172
+ versions: DD_MAJOR >= 4 ? ['>=10.2 <11.1'] : ['>=9.5 <11.1'],
173
+ file: 'dist/next-server/server/serve-static.js'
174
+ }, serveStatic => shimmer.wrap(serveStatic, 'serveStatic', wrapServeStatic))
219
175
 
220
176
  addHook({ name: 'next', versions: ['>=13.2'], file: 'dist/server/next-server.js' }, nextServer => {
221
177
  const Server = nextServer.default
222
178
 
179
+ shimmer.wrap(Server.prototype, 'handleRequest', wrapHandleRequest)
180
+ shimmer.wrap(Server.prototype, 'handleApiRequest', wrapHandleApiRequestWithMatch)
223
181
  shimmer.wrap(Server.prototype, 'renderToResponse', wrapRenderToResponse)
224
182
  shimmer.wrap(Server.prototype, 'renderErrorToResponse', wrapRenderErrorToResponse)
225
183
  shimmer.wrap(Server.prototype, 'findPageComponents', wrapFindPageComponents)
@@ -227,22 +185,6 @@ addHook({ name: 'next', versions: ['>=13.2'], file: 'dist/server/next-server.js'
227
185
  return nextServer
228
186
  })
229
187
 
230
- // these functions wrapped in all versions above 13.2 except:
231
- // 13.4.13 due to tests failing when these functions are wrapped
232
- // 13.4.14 due to it not being in the NPM registry/officially released
233
- addHook({
234
- name: 'next',
235
- versions: ['>=13.2 <13.4.13', '>=13.4.15'],
236
- file: 'dist/server/next-server.js'
237
- }, nextServer => {
238
- const Server = nextServer.default
239
-
240
- shimmer.wrap(Server.prototype, 'handleRequest', wrapHandleRequest)
241
- shimmer.wrap(Server.prototype, 'handleApiRequest', wrapHandleApiRequestWithMatch)
242
-
243
- return nextServer
244
- })
245
-
246
188
  addHook({ name: 'next', versions: ['>=11.1 <13.2'], file: 'dist/server/next-server.js' }, nextServer => {
247
189
  const Server = nextServer.default
248
190
 
@@ -39,28 +39,27 @@ function wrapQuery (query) {
39
39
  ? arguments[0]
40
40
  : { text: arguments[0] }
41
41
 
42
- // The query objects passed in can be pretty complex. They can be instances of EventEmitter.
43
- // For this reason we can't make a shallow clone of the object.
44
- // Some libraries, such as sql-template-tags, can provide a getter .text property.
45
- // For this reason we can't replace the .text property.
46
- // Instead, we create a new object, and set the original query as the prototype.
47
- // This allows any existing methods to still work and lets us easily provide a new query.
48
- let newQuery = {
49
- __ddInjectableQuery: '',
50
- get text () {
51
- return this.__ddInjectableQuery || Object.getPrototypeOf(this).text
52
- }
42
+ const textProp = Object.getOwnPropertyDescriptor(pgQuery, 'text')
43
+
44
+ // Only alter `text` property if safe to do so.
45
+ if (!textProp || textProp.configurable) {
46
+ const originalText = pgQuery.text
47
+
48
+ Object.defineProperty(pgQuery, 'text', {
49
+ get () {
50
+ return this?.__ddInjectableQuery || originalText
51
+ }
52
+ })
53
53
  }
54
- Object.setPrototypeOf(newQuery, pgQuery)
55
54
 
56
55
  return asyncResource.runInAsyncScope(() => {
57
56
  startCh.publish({
58
57
  params: this.connectionParameters,
59
- query: newQuery,
58
+ query: pgQuery,
60
59
  processId
61
60
  })
62
61
 
63
- arguments[0] = newQuery
62
+ arguments[0] = pgQuery
64
63
 
65
64
  const finish = asyncResource.bind(function (error) {
66
65
  if (error) {
@@ -73,7 +72,7 @@ function wrapQuery (query) {
73
72
  const queryQueue = this.queryQueue || this._queryQueue
74
73
  const activeQuery = this.activeQuery || this._activeQuery
75
74
 
76
- newQuery = queryQueue[queryQueue.length - 1] || activeQuery
75
+ const newQuery = queryQueue[queryQueue.length - 1] || activeQuery
77
76
 
78
77
  if (!newQuery) {
79
78
  return retval
@@ -254,7 +254,7 @@ addHook({
254
254
  addHook({
255
255
  name: '@playwright/test',
256
256
  file: 'lib/dispatcher.js',
257
- versions: ['>=1.18.0 <1.30.0']
257
+ versions: ['>=1.18.0 <1.30.0']
258
258
  }, dispatcherHook)
259
259
 
260
260
  addHook({
@@ -266,11 +266,23 @@ addHook({
266
266
  addHook({
267
267
  name: '@playwright/test',
268
268
  file: 'lib/runner/dispatcher.js',
269
- versions: ['>=1.31.0']
269
+ versions: ['>=1.31.0 <1.38.0']
270
270
  }, (dispatcher) => dispatcherHookNew(dispatcher, dispatcherRunWrapperNew))
271
271
 
272
272
  addHook({
273
273
  name: '@playwright/test',
274
274
  file: 'lib/runner/runner.js',
275
- versions: ['>=1.31.0']
275
+ versions: ['>=1.31.0 <1.38.0']
276
276
  }, runnerHook)
277
+
278
+ // From >=1.38.0
279
+ addHook({
280
+ name: 'playwright',
281
+ file: 'lib/runner/runner.js',
282
+ versions: ['>=1.38.0']
283
+ }, runnerHook)
284
+ addHook({
285
+ name: 'playwright',
286
+ file: 'lib/runner/dispatcher.js',
287
+ versions: ['>=1.38.0']
288
+ }, (dispatcher) => dispatcherHookNew(dispatcher, dispatcherRunWrapperNew))
@@ -8,13 +8,14 @@ class GraphQLResolvePlugin extends TracingPlugin {
8
8
  static get id () { return 'graphql' }
9
9
  static get operation () { return 'resolve' }
10
10
 
11
- start ({ info, context }) {
11
+ start ({ info, context, args }) {
12
12
  const path = getPath(info, this.config)
13
13
 
14
14
  if (!shouldInstrument(this.config, path)) return
15
-
16
15
  const computedPathString = path.join('.')
17
16
 
17
+ addResolver(context, info, args)
18
+
18
19
  if (this.config.collapse) {
19
20
  if (!context[collapsedPathSym]) {
20
21
  context[collapsedPathSym] = {}
@@ -108,4 +109,28 @@ function withCollapse (responsePathAsArray) {
108
109
  }
109
110
  }
110
111
 
112
+ function addResolver (context, info, args) {
113
+ if (info.rootValue && !info.rootValue[info.fieldName]) {
114
+ return
115
+ }
116
+
117
+ if (!context.resolvers) {
118
+ context.resolvers = {}
119
+ }
120
+
121
+ const resolvers = context.resolvers
122
+
123
+ if (!resolvers[info.fieldName]) {
124
+ if (args && Object.keys(args).length) {
125
+ resolvers[info.fieldName] = [args]
126
+ } else {
127
+ resolvers[info.fieldName] = []
128
+ }
129
+ } else {
130
+ if (args && Object.keys(args).length) {
131
+ resolvers[info.fieldName].push(args)
132
+ }
133
+ }
134
+ }
135
+
111
136
  module.exports = GraphQLResolvePlugin
@@ -50,7 +50,6 @@ class NextPlugin extends ServerPlugin {
50
50
 
51
51
  const span = store.span
52
52
  const error = span.context()._tags['error']
53
- const page = span.context()._tags['next.page']
54
53
 
55
54
  if (!this.config.validateStatus(res.statusCode) && !error) {
56
55
  span.setTag('error', true)
@@ -60,14 +59,12 @@ class NextPlugin extends ServerPlugin {
60
59
  'http.status_code': res.statusCode
61
60
  })
62
61
 
63
- if (page) web.setRoute(req, page)
64
-
65
62
  this.config.hooks.request(span, req, res)
66
63
 
67
64
  span.finish()
68
65
  }
69
66
 
70
- pageLoad ({ page }) {
67
+ pageLoad ({ page, isAppPath }) {
71
68
  const store = storage.getStore()
72
69
 
73
70
  if (!store) return
@@ -77,15 +74,27 @@ class NextPlugin extends ServerPlugin {
77
74
 
78
75
  // Only use error page names if there's not already a name
79
76
  const current = span.context()._tags['next.page']
80
- if (current && (page === '/404' || page === '/500' || page === '/_error')) {
77
+ if (current && ['/404', '/500', '/_error', '/_not-found'].includes(page)) {
81
78
  return
82
79
  }
83
80
 
81
+ // remove ending /route or /page for appDir projects
82
+ if (isAppPath) page = page.substring(0, page.lastIndexOf('/'))
83
+
84
+ // This is for static files whose 'page' includes the whole file path
85
+ // For normal page matches, like /api/hello/[name] and a req.url like /api/hello/world,
86
+ // nothing should happen
87
+ // For page matches like /User/something/public/text.txt and req.url like /text.txt,
88
+ // it should disregard the extra absolute path Next.js sometimes sets
89
+ if (page.includes(req.url)) page = req.url
90
+
84
91
  span.addTags({
85
92
  [COMPONENT]: this.constructor.id,
86
93
  'resource.name': `${req.method} ${page}`.trim(),
87
94
  'next.page': page
88
95
  })
96
+
97
+ web.setRoute(req, page)
89
98
  }
90
99
 
91
100
  configure (config) {
@@ -12,6 +12,7 @@ module.exports = {
12
12
  HTTP_INCOMING_RESPONSE_CODE: 'server.response.status',
13
13
  HTTP_INCOMING_RESPONSE_HEADERS: 'server.response.headers.no_cookies',
14
14
  // TODO: 'server.response.trailers',
15
+ HTTP_INCOMING_GRAPHQL_RESOLVERS: 'graphql.server.all_resolvers',
15
16
 
16
17
  HTTP_CLIENT_IP: 'http.client_ip',
17
18
 
@@ -5,6 +5,8 @@ const dc = require('../../../diagnostics_channel')
5
5
  // TODO: use TBD naming convention
6
6
  module.exports = {
7
7
  bodyParser: dc.channel('datadog:body-parser:read:finish'),
8
+ cookieParser: dc.channel('datadog:cookie-parser:read:finish'),
9
+ graphqlFinishExecute: dc.channel('apm:graphql:execute:finish'),
8
10
  incomingHttpRequestStart: dc.channel('dd-trace:incomingHttpRequestStart'),
9
11
  incomingHttpRequestEnd: dc.channel('dd-trace:incomingHttpRequestEnd'),
10
12
  passportVerify: dc.channel('datadog:passport:verify:finish'),