dd-trace 4.11.1 → 4.13.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 +4 -9
- package/index.d.ts +43 -0
- package/package.json +3 -2
- package/packages/datadog-esbuild/index.js +29 -9
- package/packages/datadog-instrumentations/src/cucumber.js +30 -11
- package/packages/datadog-instrumentations/src/jest.js +22 -11
- package/packages/datadog-instrumentations/src/mocha.js +30 -8
- package/packages/datadog-instrumentations/src/next.js +102 -16
- package/packages/datadog-instrumentations/src/openai.js +1 -1
- package/packages/datadog-instrumentations/src/pg.js +46 -0
- package/packages/datadog-plugin-cucumber/src/index.js +14 -2
- package/packages/datadog-plugin-cypress/src/plugin.js +17 -8
- package/packages/datadog-plugin-graphql/src/index.js +3 -3
- package/packages/datadog-plugin-jest/src/index.js +10 -2
- package/packages/datadog-plugin-jest/src/util.js +10 -4
- package/packages/datadog-plugin-mocha/src/index.js +14 -2
- package/packages/datadog-plugin-next/src/index.js +11 -3
- package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +19 -4
- package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +1 -1
- package/packages/dd-trace/src/appsec/iast/telemetry/index.js +14 -5
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +120 -10
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +0 -1
- package/packages/dd-trace/src/dogstatsd.js +65 -5
- package/packages/dd-trace/src/exporters/agent/writer.js +9 -9
- package/packages/dd-trace/src/exporters/common/request.js +13 -4
- package/packages/dd-trace/src/opentracing/span.js +13 -13
- package/packages/dd-trace/src/opentracing/tracer.js +3 -3
- package/packages/dd-trace/src/plugins/ci_plugin.js +22 -1
- package/packages/dd-trace/src/plugins/util/test.js +18 -1
- package/packages/dd-trace/src/profiling/config.js +3 -1
- package/packages/dd-trace/src/profiling/profilers/wall.js +23 -7
- package/packages/dd-trace/src/proxy.js +23 -2
- package/packages/dd-trace/src/ritm.js +10 -2
- /package/packages/dd-trace/src/{metrics.js → runtime_metrics.js} +0 -0
package/README.md
CHANGED
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/dd-trace)
|
|
4
4
|
[](https://www.npmjs.com/package/dd-trace/v/latest-node12)
|
|
5
|
-
[](https://www.npmjs.com/package/dd-trace/v/latest-node12)
|
|
6
|
-
[](https://www.npmjs.com/package/dd-trace/v/dev)
|
|
7
5
|
[](https://codecov.io/gh/DataDog/dd-trace-js)
|
|
8
6
|
|
|
9
7
|
<img align="right" src="https://user-images.githubusercontent.com/551402/208212084-1d0c07e2-4135-4c61-b2da-8f2fddbc66ed.png" alt="Bits the dog JavaScript" width="200px"/>
|
|
@@ -28,12 +26,12 @@ Most of the documentation for `dd-trace` is available on these webpages:
|
|
|
28
26
|
| Release Line | Latest Version | Node.js | Status |Initial Release | End of Life |
|
|
29
27
|
| :---: | :---: | :---: | :---: | :---: | :---: |
|
|
30
28
|
| [`v1`](https://github.com/DataDog/dd-trace-js/tree/v1.x) |  | `>= v12` | **End of Life** | 2021-07-13 | 2022-02-25 |
|
|
31
|
-
| [`v2`](https://github.com/DataDog/dd-trace-js/tree/v2.x) |  | `>= v12` | **
|
|
29
|
+
| [`v2`](https://github.com/DataDog/dd-trace-js/tree/v2.x) |  | `>= v12` | **End of Life** | 2022-01-28 | 2023-08-15 |
|
|
32
30
|
| [`v3`](https://github.com/DataDog/dd-trace-js/tree/v3.x) |  | `>= v14` | **Maintenance** | 2022-08-15 | 2024-05-15 |
|
|
33
31
|
| [`v4`](https://github.com/DataDog/dd-trace-js/tree/v4.x) |  | `>= v16` | **Current** | 2023-05-12 | Unknown |
|
|
34
32
|
|
|
35
|
-
We currently maintain
|
|
36
|
-
Features and bug fixes that are merged are released to the `v4` line and, if appropriate, also the `
|
|
33
|
+
We currently maintain two release lines, namely `v3` and `v4`.
|
|
34
|
+
Features and bug fixes that are merged are released to the `v4` line and, if appropriate, also the `v3` line.
|
|
37
35
|
|
|
38
36
|
For any new projects it is recommended to use the `v4` release line:
|
|
39
37
|
|
|
@@ -42,15 +40,12 @@ $ npm install dd-trace
|
|
|
42
40
|
$ yarn add dd-trace
|
|
43
41
|
```
|
|
44
42
|
|
|
45
|
-
However, existing projects that already use the `
|
|
43
|
+
However, existing projects that already use the `v3` release line, or projects that need to support EOL versions of Node.js, may continue to use these release lines.
|
|
46
44
|
This is done by specifying the version when installing the package.
|
|
47
|
-
Note that we also publish to npm using a `latest-node12` and `latest-node14` tag that can also be used for install:
|
|
48
45
|
|
|
49
46
|
```sh
|
|
50
47
|
$ npm install dd-trace@3
|
|
51
48
|
$ yarn add dd-trace@3
|
|
52
|
-
$ npm install dd-trace@latest-node14
|
|
53
|
-
$ yarn add dd-trace@latest-node14
|
|
54
49
|
```
|
|
55
50
|
|
|
56
51
|
Any backwards-breaking functionality that is introduced into the library will result in an increase of the major version of the library and therefore a new release line.
|
package/index.d.ts
CHANGED
|
@@ -121,6 +121,8 @@ export declare interface Tracer extends opentracing.Tracer {
|
|
|
121
121
|
appsec: Appsec;
|
|
122
122
|
|
|
123
123
|
TracerProvider: opentelemetry.TracerProvider;
|
|
124
|
+
|
|
125
|
+
dogstatsd: DogStatsD;
|
|
124
126
|
}
|
|
125
127
|
|
|
126
128
|
export declare interface TraceOptions extends Analyzable {
|
|
@@ -642,6 +644,47 @@ export declare interface User {
|
|
|
642
644
|
[key: string]: string | undefined
|
|
643
645
|
}
|
|
644
646
|
|
|
647
|
+
export declare interface DogStatsD {
|
|
648
|
+
/**
|
|
649
|
+
* Increments a metric by the specified value, optionally specifying tags.
|
|
650
|
+
* @param {string} stat The dot-separated metric name.
|
|
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.
|
|
653
|
+
*/
|
|
654
|
+
increment(stat: string, value?: number, tags?: { [tag: string]: string|number }): void
|
|
655
|
+
|
|
656
|
+
/**
|
|
657
|
+
* Decrements a metric by the specified value, optionally specifying tags.
|
|
658
|
+
* @param {string} stat The dot-separated metric name.
|
|
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.
|
|
661
|
+
*/
|
|
662
|
+
decrement(stat: string, value?: number, tags?: { [tag: string]: string|number }): void
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
* Sets a distribution value, optionally specifying tags.
|
|
666
|
+
* @param {string} stat The dot-separated metric name.
|
|
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.
|
|
669
|
+
*/
|
|
670
|
+
distribution(stat: string, value?: number, tags?: { [tag: string]: string|number }): void
|
|
671
|
+
|
|
672
|
+
/**
|
|
673
|
+
* Sets a gauge value, optionally specifying tags.
|
|
674
|
+
* @param {string} stat The dot-separated metric name.
|
|
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.
|
|
677
|
+
*/
|
|
678
|
+
gauge(stat: string, value?: number, tags?: { [tag: string]: string|number }): void
|
|
679
|
+
|
|
680
|
+
/**
|
|
681
|
+
* Forces any unsent metrics to be sent
|
|
682
|
+
*
|
|
683
|
+
* @beta This method is experimental and could be removed in future versions.
|
|
684
|
+
*/
|
|
685
|
+
flush(): void
|
|
686
|
+
}
|
|
687
|
+
|
|
645
688
|
export declare interface Appsec {
|
|
646
689
|
/**
|
|
647
690
|
* Links a successful login event to the current trace. Will link the passed user to the current trace with Appsec.setUser() internally.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dd-trace",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.13.0",
|
|
4
4
|
"description": "Datadog APM tracing client for JavaScript",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"typings": "index.d.ts",
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
"test:integration:cypress": "mocha --colors --timeout 30000 \"integration-tests/cypress/*.spec.js\"",
|
|
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
|
+
"test:integration:plugins": "mocha --colors --timeout 30000 \"packages/datadog-plugin-*/test/integration-test/*.spec.js\"",
|
|
40
41
|
"test:shimmer": "mocha --colors 'packages/datadog-shimmer/test/**/*.spec.js'",
|
|
41
42
|
"test:shimmer:ci": "nyc --no-clean --include 'packages/datadog-shimmer/src/**/*.js' -- npm run test:shimmer",
|
|
42
43
|
"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'",
|
|
@@ -70,7 +71,7 @@
|
|
|
70
71
|
"@datadog/native-iast-rewriter": "2.0.1",
|
|
71
72
|
"@datadog/native-iast-taint-tracking": "1.5.0",
|
|
72
73
|
"@datadog/native-metrics": "^2.0.0",
|
|
73
|
-
"@datadog/pprof": "3.
|
|
74
|
+
"@datadog/pprof": "3.2.0",
|
|
74
75
|
"@datadog/sketches-js": "^2.1.0",
|
|
75
76
|
"@opentelemetry/api": "^1.0.0",
|
|
76
77
|
"@opentelemetry/core": "^1.14.0",
|
|
@@ -50,21 +50,41 @@ for (const pkg of INSTRUMENTED) {
|
|
|
50
50
|
module.exports.name = 'datadog-esbuild'
|
|
51
51
|
|
|
52
52
|
module.exports.setup = function (build) {
|
|
53
|
+
const externalModules = new Set(build.initialOptions.external || [])
|
|
53
54
|
build.onResolve({ filter: /.*/ }, args => {
|
|
55
|
+
if (externalModules.has(args.path)) {
|
|
56
|
+
// Internal Node.js packages will still be instrumented via require()
|
|
57
|
+
if (DEBUG) console.log(`EXTERNAL: ${args.path}`)
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// TODO: Should this also check for namespace === 'file'?
|
|
62
|
+
if (args.path.startsWith('.') && !args.importer.includes('node_modules/')) {
|
|
63
|
+
// This is local application code, not an instrumented package
|
|
64
|
+
if (DEBUG) console.log(`LOCAL: ${args.path}`)
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// TODO: Should this also check for namespace === 'file'?
|
|
69
|
+
if (args.path.startsWith('@') && !args.importer.includes('node_modules/')) {
|
|
70
|
+
// This is the Next.js convention for loading local files
|
|
71
|
+
if (DEBUG) console.log(`@LOCAL: ${args.path}`)
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
|
|
54
75
|
let fullPathToModule
|
|
55
76
|
try {
|
|
56
77
|
fullPathToModule = dotFriendlyResolve(args.path, args.resolveDir)
|
|
57
78
|
} catch (err) {
|
|
58
|
-
console.warn(`Unable to find "${args.path}". Is the package dead code?`)
|
|
79
|
+
console.warn(`MISSING: Unable to find "${args.path}". Is the package dead code?`)
|
|
59
80
|
return
|
|
60
81
|
}
|
|
61
82
|
const extracted = extractPackageAndModulePath(fullPathToModule)
|
|
62
|
-
const packageName = args.path
|
|
63
83
|
|
|
64
84
|
const internal = builtins.has(args.path)
|
|
65
85
|
|
|
66
86
|
if (args.namespace === 'file' && (
|
|
67
|
-
modulesOfInterest.has(
|
|
87
|
+
modulesOfInterest.has(args.path) || modulesOfInterest.has(`${extracted.pkg}/${extracted.path}`))
|
|
68
88
|
) {
|
|
69
89
|
// The file namespace is used when requiring files from disk in userland
|
|
70
90
|
|
|
@@ -74,7 +94,7 @@ module.exports.setup = function (build) {
|
|
|
74
94
|
} catch (err) {
|
|
75
95
|
if (err.code === 'MODULE_NOT_FOUND') {
|
|
76
96
|
if (!internal) {
|
|
77
|
-
console.warn(`Unable to find "${extracted.pkg}/package.json". Is the package dead code?`)
|
|
97
|
+
console.warn(`MISSING: Unable to find "${extracted.pkg}/package.json". Is the package dead code?`)
|
|
78
98
|
}
|
|
79
99
|
return
|
|
80
100
|
} else {
|
|
@@ -84,7 +104,7 @@ module.exports.setup = function (build) {
|
|
|
84
104
|
|
|
85
105
|
const packageJson = require(pathToPackageJson)
|
|
86
106
|
|
|
87
|
-
if (DEBUG) console.log(`RESOLVE ${
|
|
107
|
+
if (DEBUG) console.log(`RESOLVE: ${args.path}@${packageJson.version}`)
|
|
88
108
|
|
|
89
109
|
// https://esbuild.github.io/plugins/#on-resolve-arguments
|
|
90
110
|
return {
|
|
@@ -95,17 +115,17 @@ module.exports.setup = function (build) {
|
|
|
95
115
|
pkg: extracted.pkg,
|
|
96
116
|
path: extracted.path,
|
|
97
117
|
full: fullPathToModule,
|
|
98
|
-
raw:
|
|
118
|
+
raw: args.path,
|
|
99
119
|
internal
|
|
100
120
|
}
|
|
101
121
|
}
|
|
102
122
|
} else if (args.namespace === NAMESPACE) {
|
|
103
123
|
// The datadog namespace is used when requiring files that are injected during the onLoad stage
|
|
104
124
|
|
|
105
|
-
if (builtins.has(
|
|
125
|
+
if (builtins.has(args.path)) return
|
|
106
126
|
|
|
107
127
|
return {
|
|
108
|
-
path: require.resolve(
|
|
128
|
+
path: require.resolve(args.path, { paths: [ args.resolveDir ] }),
|
|
109
129
|
namespace: 'file'
|
|
110
130
|
}
|
|
111
131
|
}
|
|
@@ -114,7 +134,7 @@ module.exports.setup = function (build) {
|
|
|
114
134
|
build.onLoad({ filter: /.*/, namespace: NAMESPACE }, args => {
|
|
115
135
|
const data = args.pluginData
|
|
116
136
|
|
|
117
|
-
if (DEBUG) console.log(`LOAD ${data.pkg}@${data.version}, pkg "${data.path}"`)
|
|
137
|
+
if (DEBUG) console.log(`LOAD: ${data.pkg}@${data.version}, pkg "${data.path}"`)
|
|
118
138
|
|
|
119
139
|
const path = data.raw !== data.pkg
|
|
120
140
|
? `${data.pkg}/${data.path}`
|
|
@@ -21,6 +21,8 @@ const skippableSuitesCh = channel('ci:cucumber:test-suite:skippable')
|
|
|
21
21
|
const sessionStartCh = channel('ci:cucumber:session:start')
|
|
22
22
|
const sessionFinishCh = channel('ci:cucumber:session:finish')
|
|
23
23
|
|
|
24
|
+
const itrSkippedSuitesCh = channel('ci:cucumber:itr:skipped-suites')
|
|
25
|
+
|
|
24
26
|
const {
|
|
25
27
|
getCoveredFilenamesFromCoverage,
|
|
26
28
|
resetCoverage,
|
|
@@ -37,7 +39,6 @@ const patched = new WeakSet()
|
|
|
37
39
|
|
|
38
40
|
let pickleByFile = {}
|
|
39
41
|
const pickleResultByFile = {}
|
|
40
|
-
let isSuitesSkipped = false
|
|
41
42
|
|
|
42
43
|
function getSuiteStatusFromTestStatuses (testStatuses) {
|
|
43
44
|
if (testStatuses.some(status => status === 'fail')) {
|
|
@@ -216,11 +217,18 @@ addHook({
|
|
|
216
217
|
file: 'lib/runtime/test_case_runner.js'
|
|
217
218
|
}, testCaseHook)
|
|
218
219
|
|
|
219
|
-
function
|
|
220
|
-
return runtime.pickleIds.
|
|
220
|
+
function getFilteredPickles (runtime, suitesToSkip) {
|
|
221
|
+
return runtime.pickleIds.reduce((acc, pickleId) => {
|
|
221
222
|
const test = runtime.eventDataCollector.getPickle(pickleId)
|
|
222
|
-
|
|
223
|
-
|
|
223
|
+
const testSuitePath = getTestSuitePath(test.uri, process.cwd())
|
|
224
|
+
const isSkipped = suitesToSkip.includes(testSuitePath)
|
|
225
|
+
if (isSkipped) {
|
|
226
|
+
acc.skippedSuites.add(testSuitePath)
|
|
227
|
+
} else {
|
|
228
|
+
acc.picklesToRun.push(pickleId)
|
|
229
|
+
}
|
|
230
|
+
return acc
|
|
231
|
+
}, { skippedSuites: new Set(), picklesToRun: [] })
|
|
224
232
|
}
|
|
225
233
|
|
|
226
234
|
function getPickleByFile (runtime) {
|
|
@@ -239,7 +247,7 @@ addHook({
|
|
|
239
247
|
name: '@cucumber/cucumber',
|
|
240
248
|
versions: ['>=7.0.0'],
|
|
241
249
|
file: 'lib/runtime/index.js'
|
|
242
|
-
}, (runtimePackage,
|
|
250
|
+
}, (runtimePackage, frameworkVersion) => {
|
|
243
251
|
shimmer.wrap(runtimePackage.default.prototype, 'start', start => async function () {
|
|
244
252
|
const asyncResource = new AsyncResource('bound-anonymous-fn')
|
|
245
253
|
let onDone
|
|
@@ -263,11 +271,16 @@ addHook({
|
|
|
263
271
|
})
|
|
264
272
|
|
|
265
273
|
const { err, skippableSuites } = await skippableSuitesPromise
|
|
274
|
+
let skippedSuites = []
|
|
275
|
+
let isSuitesSkipped = false
|
|
266
276
|
|
|
267
277
|
if (!err) {
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
this.pickleIds
|
|
278
|
+
const filteredPickles = getFilteredPickles(this, skippableSuites)
|
|
279
|
+
const { picklesToRun } = filteredPickles
|
|
280
|
+
isSuitesSkipped = picklesToRun.length !== this.pickleIds.length
|
|
281
|
+
this.pickleIds = picklesToRun
|
|
282
|
+
|
|
283
|
+
skippedSuites = Array.from(filteredPickles.skippedSuites)
|
|
271
284
|
}
|
|
272
285
|
|
|
273
286
|
pickleByFile = getPickleByFile(this)
|
|
@@ -276,8 +289,13 @@ addHook({
|
|
|
276
289
|
const command = process.env.npm_lifecycle_script || `cucumber-js ${processArgv}`
|
|
277
290
|
|
|
278
291
|
asyncResource.runInAsyncScope(() => {
|
|
279
|
-
sessionStartCh.publish({ command, frameworkVersion
|
|
292
|
+
sessionStartCh.publish({ command, frameworkVersion })
|
|
280
293
|
})
|
|
294
|
+
|
|
295
|
+
if (!err && skippedSuites.length) {
|
|
296
|
+
itrSkippedSuitesCh.publish({ skippedSuites, frameworkVersion })
|
|
297
|
+
}
|
|
298
|
+
|
|
281
299
|
const success = await start.apply(this, arguments)
|
|
282
300
|
|
|
283
301
|
let testCodeCoverageLinesTotal
|
|
@@ -296,7 +314,8 @@ addHook({
|
|
|
296
314
|
sessionFinishCh.publish({
|
|
297
315
|
status: success ? 'pass' : 'fail',
|
|
298
316
|
isSuitesSkipped,
|
|
299
|
-
testCodeCoverageLinesTotal
|
|
317
|
+
testCodeCoverageLinesTotal,
|
|
318
|
+
numSkippedSuites: skippedSuites.length
|
|
300
319
|
})
|
|
301
320
|
})
|
|
302
321
|
return success
|
|
@@ -39,10 +39,13 @@ const testErrCh = channel('ci:jest:test:err')
|
|
|
39
39
|
const skippableSuitesCh = channel('ci:jest:test-suite:skippable')
|
|
40
40
|
const jestItrConfigurationCh = channel('ci:jest:itr-configuration')
|
|
41
41
|
|
|
42
|
+
const itrSkippedSuitesCh = channel('ci:jest:itr:skipped-suites')
|
|
43
|
+
|
|
42
44
|
let skippableSuites = []
|
|
43
45
|
let isCodeCoverageEnabled = false
|
|
44
46
|
let isSuitesSkippingEnabled = false
|
|
45
47
|
let isSuitesSkipped = false
|
|
48
|
+
let numSkippedSuites = 0
|
|
46
49
|
|
|
47
50
|
const sessionAsyncResource = new AsyncResource('bound-anonymous-fn')
|
|
48
51
|
|
|
@@ -102,7 +105,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
102
105
|
await super.handleTestEvent(event, state)
|
|
103
106
|
}
|
|
104
107
|
|
|
105
|
-
const setNameToParams = (name, params) => { this.nameToParams[name] = params }
|
|
108
|
+
const setNameToParams = (name, params) => { this.nameToParams[name] = [...params] }
|
|
106
109
|
|
|
107
110
|
if (event.name === 'setup') {
|
|
108
111
|
if (this.global.test) {
|
|
@@ -191,7 +194,7 @@ addHook({
|
|
|
191
194
|
addHook({
|
|
192
195
|
name: '@jest/test-sequencer',
|
|
193
196
|
versions: ['>=24.8.0']
|
|
194
|
-
}, sequencerPackage => {
|
|
197
|
+
}, (sequencerPackage, frameworkVersion) => {
|
|
195
198
|
shimmer.wrap(sequencerPackage.default.prototype, 'shard', shard => function () {
|
|
196
199
|
const shardedTests = shard.apply(this, arguments)
|
|
197
200
|
|
|
@@ -202,13 +205,15 @@ addHook({
|
|
|
202
205
|
const [test] = shardedTests
|
|
203
206
|
const rootDir = test && test.context && test.context.config && test.context.config.rootDir
|
|
204
207
|
|
|
205
|
-
const
|
|
208
|
+
const { skippedSuites, suitesToRun } = getJestSuitesToRun(skippableSuites, shardedTests, rootDir || process.cwd())
|
|
206
209
|
|
|
207
|
-
isSuitesSkipped =
|
|
210
|
+
isSuitesSkipped = suitesToRun.length !== shardedTests.length
|
|
211
|
+
numSkippedSuites = skippedSuites.length
|
|
208
212
|
|
|
209
|
-
|
|
213
|
+
itrSkippedSuitesCh.publish({ skippedSuites, frameworkVersion })
|
|
210
214
|
|
|
211
|
-
|
|
215
|
+
skippableSuites = []
|
|
216
|
+
return suitesToRun
|
|
212
217
|
})
|
|
213
218
|
return sequencerPackage
|
|
214
219
|
})
|
|
@@ -279,10 +284,13 @@ function cliWrapper (cli, jestVersion) {
|
|
|
279
284
|
isSuitesSkipped,
|
|
280
285
|
isSuitesSkippingEnabled,
|
|
281
286
|
isCodeCoverageEnabled,
|
|
282
|
-
testCodeCoverageLinesTotal
|
|
287
|
+
testCodeCoverageLinesTotal,
|
|
288
|
+
numSkippedSuites
|
|
283
289
|
})
|
|
284
290
|
})
|
|
285
291
|
|
|
292
|
+
numSkippedSuites = 0
|
|
293
|
+
|
|
286
294
|
return result
|
|
287
295
|
})
|
|
288
296
|
|
|
@@ -468,7 +476,7 @@ addHook({
|
|
|
468
476
|
name: '@jest/core',
|
|
469
477
|
versions: ['>=24.8.0'],
|
|
470
478
|
file: 'build/SearchSource.js'
|
|
471
|
-
}, searchSourcePackage => {
|
|
479
|
+
}, (searchSourcePackage, frameworkVersion) => {
|
|
472
480
|
const SearchSource = searchSourcePackage.default ? searchSourcePackage.default : searchSourcePackage
|
|
473
481
|
|
|
474
482
|
shimmer.wrap(SearchSource.prototype, 'getTestPaths', getTestPaths => async function () {
|
|
@@ -492,13 +500,16 @@ addHook({
|
|
|
492
500
|
const testPaths = await getTestPaths.apply(this, arguments)
|
|
493
501
|
const { tests } = testPaths
|
|
494
502
|
|
|
495
|
-
const
|
|
503
|
+
const { skippedSuites, suitesToRun } = getJestSuitesToRun(skippableSuites, tests, rootDir)
|
|
504
|
+
|
|
505
|
+
isSuitesSkipped = suitesToRun.length !== tests.length
|
|
506
|
+
numSkippedSuites = skippedSuites.length
|
|
496
507
|
|
|
497
|
-
|
|
508
|
+
itrSkippedSuitesCh.publish({ skippedSuites, frameworkVersion })
|
|
498
509
|
|
|
499
510
|
skippableSuites = []
|
|
500
511
|
|
|
501
|
-
return { ...testPaths, tests:
|
|
512
|
+
return { ...testPaths, tests: suitesToRun }
|
|
502
513
|
})
|
|
503
514
|
|
|
504
515
|
return searchSourcePackage
|
|
@@ -30,6 +30,8 @@ const testSuiteFinishCh = channel('ci:mocha:test-suite:finish')
|
|
|
30
30
|
const testSuiteErrorCh = channel('ci:mocha:test-suite:error')
|
|
31
31
|
const testSuiteCodeCoverageCh = channel('ci:mocha:test-suite:code-coverage')
|
|
32
32
|
|
|
33
|
+
const itrSkippedSuitesCh = channel('ci:mocha:itr:skipped-suites')
|
|
34
|
+
|
|
33
35
|
// TODO: remove when root hooks and fixtures are implemented
|
|
34
36
|
const patched = new WeakSet()
|
|
35
37
|
|
|
@@ -47,6 +49,7 @@ const originalCoverageMap = createCoverageMap()
|
|
|
47
49
|
let suitesToSkip = []
|
|
48
50
|
let frameworkVersion
|
|
49
51
|
let isSuitesSkipped = false
|
|
52
|
+
let skippedSuites = []
|
|
50
53
|
|
|
51
54
|
function getSuitesByTestFile (root) {
|
|
52
55
|
const suitesByTestFile = {}
|
|
@@ -97,10 +100,17 @@ function getTestAsyncResource (test) {
|
|
|
97
100
|
return testToAr.get(originalFn)
|
|
98
101
|
}
|
|
99
102
|
|
|
100
|
-
function
|
|
101
|
-
return originalSuites.
|
|
102
|
-
|
|
103
|
-
|
|
103
|
+
function getFilteredSuites (originalSuites) {
|
|
104
|
+
return originalSuites.reduce((acc, suite) => {
|
|
105
|
+
const testPath = getTestSuitePath(suite.file, process.cwd())
|
|
106
|
+
const shouldSkip = suitesToSkip.includes(testPath)
|
|
107
|
+
if (shouldSkip) {
|
|
108
|
+
acc.skippedSuites.add(testPath)
|
|
109
|
+
} else {
|
|
110
|
+
acc.suitesToRun.push(suite)
|
|
111
|
+
}
|
|
112
|
+
return acc
|
|
113
|
+
}, { suitesToRun: [], skippedSuites: new Set() })
|
|
104
114
|
}
|
|
105
115
|
|
|
106
116
|
function mochaHook (Runner) {
|
|
@@ -137,13 +147,21 @@ function mochaHook (Runner) {
|
|
|
137
147
|
global.__coverage__ = fromCoverageMapToCoverage(originalCoverageMap)
|
|
138
148
|
}
|
|
139
149
|
|
|
140
|
-
testSessionFinishCh.publish({
|
|
150
|
+
testSessionFinishCh.publish({
|
|
151
|
+
status,
|
|
152
|
+
isSuitesSkipped,
|
|
153
|
+
testCodeCoverageLinesTotal,
|
|
154
|
+
numSkippedSuites: skippedSuites.length
|
|
155
|
+
})
|
|
141
156
|
}))
|
|
142
157
|
|
|
143
158
|
this.once('start', testRunAsyncResource.bind(function () {
|
|
144
159
|
const processArgv = process.argv.slice(2).join(' ')
|
|
145
160
|
const command = `mocha ${processArgv}`
|
|
146
161
|
testSessionStartCh.publish({ command, frameworkVersion })
|
|
162
|
+
if (skippedSuites.length) {
|
|
163
|
+
itrSkippedSuitesCh.publish({ skippedSuites, frameworkVersion })
|
|
164
|
+
}
|
|
147
165
|
}))
|
|
148
166
|
|
|
149
167
|
this.on('suite', function (suite) {
|
|
@@ -359,9 +377,13 @@ addHook({
|
|
|
359
377
|
suitesToSkip = skippableSuites
|
|
360
378
|
}
|
|
361
379
|
// We remove the suites that we skip through ITR
|
|
362
|
-
const
|
|
363
|
-
|
|
364
|
-
|
|
380
|
+
const filteredSuites = getFilteredSuites(runner.suite.suites)
|
|
381
|
+
const { suitesToRun } = filteredSuites
|
|
382
|
+
|
|
383
|
+
isSuitesSkipped = suitesToRun.length !== runner.suite.suites.length
|
|
384
|
+
runner.suite.suites = suitesToRun
|
|
385
|
+
|
|
386
|
+
skippedSuites = Array.from(filteredSuites.skippedSuites)
|
|
365
387
|
|
|
366
388
|
global.run()
|
|
367
389
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// TODO: either instrument all or none of the render functions
|
|
4
4
|
|
|
5
|
-
const { channel, addHook
|
|
5
|
+
const { channel, addHook } = require('./helpers/instrument')
|
|
6
6
|
const shimmer = require('../../datadog-shimmer')
|
|
7
7
|
const { DD_MAJOR } = require('../../../version')
|
|
8
8
|
|
|
@@ -11,7 +11,8 @@ const finishChannel = channel('apm:next:request:finish')
|
|
|
11
11
|
const errorChannel = channel('apm:next:request:error')
|
|
12
12
|
const pageLoadChannel = channel('apm:next:page:load')
|
|
13
13
|
|
|
14
|
-
const
|
|
14
|
+
const requests = new WeakSet()
|
|
15
|
+
const requestToNextjsPagePath = new WeakMap()
|
|
15
16
|
|
|
16
17
|
function wrapHandleRequest (handleRequest) {
|
|
17
18
|
return function (req, res, pathname, query) {
|
|
@@ -105,38 +106,91 @@ function getPageFromPath (page, dynamicRoutes = []) {
|
|
|
105
106
|
}
|
|
106
107
|
|
|
107
108
|
function instrument (req, res, handler) {
|
|
108
|
-
|
|
109
|
+
req = req.originalRequest || req
|
|
110
|
+
res = res.originalResponse || res
|
|
109
111
|
|
|
110
|
-
|
|
112
|
+
if (requests.has(req)) return handler()
|
|
111
113
|
|
|
112
|
-
|
|
114
|
+
requests.add(req)
|
|
113
115
|
|
|
114
|
-
|
|
115
|
-
startChannel.publish({ req, res })
|
|
116
|
+
const ctx = { req, res }
|
|
116
117
|
|
|
118
|
+
return startChannel.runStores(ctx, () => {
|
|
117
119
|
try {
|
|
118
|
-
const promise = handler()
|
|
120
|
+
const promise = handler(ctx)
|
|
119
121
|
|
|
120
122
|
// promise should only reject when propagateError is true:
|
|
121
123
|
// https://github.com/vercel/next.js/blob/cee656238a/packages/next/server/api-utils/node.ts#L547
|
|
122
124
|
return promise.then(
|
|
123
|
-
result => finish(
|
|
124
|
-
err => finish(
|
|
125
|
+
result => finish(ctx, result),
|
|
126
|
+
err => finish(ctx, null, err)
|
|
125
127
|
)
|
|
126
128
|
} catch (e) {
|
|
127
129
|
// this will probably never happen as the handler caller is an async function:
|
|
128
130
|
// https://github.com/vercel/next.js/blob/cee656238a/packages/next/server/api-utils/node.ts#L420
|
|
129
|
-
return finish(
|
|
131
|
+
return finish(ctx, null, e)
|
|
130
132
|
}
|
|
131
133
|
})
|
|
132
134
|
}
|
|
133
135
|
|
|
134
|
-
function
|
|
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 })
|
|
161
|
+
|
|
162
|
+
return result
|
|
163
|
+
})
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
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
|
+
function finish (ctx, result, err) {
|
|
135
188
|
if (err) {
|
|
136
|
-
|
|
189
|
+
ctx.error = err
|
|
190
|
+
errorChannel.publish(ctx)
|
|
137
191
|
}
|
|
138
192
|
|
|
139
|
-
finishChannel.publish(
|
|
193
|
+
finishChannel.publish(ctx)
|
|
140
194
|
|
|
141
195
|
if (err) {
|
|
142
196
|
throw err
|
|
@@ -145,11 +199,27 @@ function finish (req, res, result, err) {
|
|
|
145
199
|
return result
|
|
146
200
|
}
|
|
147
201
|
|
|
202
|
+
addHook({
|
|
203
|
+
name: 'next',
|
|
204
|
+
versions: ['>=13.4.13'],
|
|
205
|
+
file: 'dist/server/lib/router-utils/resolve-routes.js'
|
|
206
|
+
}, resolveRoutesModule => shimmer.wrap(resolveRoutesModule, 'getResolveRoutes', wrapGetResolveRoutes))
|
|
207
|
+
|
|
208
|
+
addHook({
|
|
209
|
+
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))
|
|
219
|
+
|
|
148
220
|
addHook({ name: 'next', versions: ['>=13.2'], file: 'dist/server/next-server.js' }, nextServer => {
|
|
149
221
|
const Server = nextServer.default
|
|
150
222
|
|
|
151
|
-
shimmer.wrap(Server.prototype, 'handleRequest', wrapHandleRequest)
|
|
152
|
-
shimmer.wrap(Server.prototype, 'handleApiRequest', wrapHandleApiRequestWithMatch)
|
|
153
223
|
shimmer.wrap(Server.prototype, 'renderToResponse', wrapRenderToResponse)
|
|
154
224
|
shimmer.wrap(Server.prototype, 'renderErrorToResponse', wrapRenderErrorToResponse)
|
|
155
225
|
shimmer.wrap(Server.prototype, 'findPageComponents', wrapFindPageComponents)
|
|
@@ -157,6 +227,22 @@ addHook({ name: 'next', versions: ['>=13.2'], file: 'dist/server/next-server.js'
|
|
|
157
227
|
return nextServer
|
|
158
228
|
})
|
|
159
229
|
|
|
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
|
+
|
|
160
246
|
addHook({ name: 'next', versions: ['>=11.1 <13.2'], file: 'dist/server/next-server.js' }, nextServer => {
|
|
161
247
|
const Server = nextServer.default
|
|
162
248
|
|
|
@@ -10,7 +10,7 @@ const startCh = channel('apm:openai:request:start')
|
|
|
10
10
|
const finishCh = channel('apm:openai:request:finish')
|
|
11
11
|
const errorCh = channel('apm:openai:request:error')
|
|
12
12
|
|
|
13
|
-
addHook({ name: 'openai', file: 'dist/api.js', versions: ['>=3.0.0'] }, exports => {
|
|
13
|
+
addHook({ name: 'openai', file: 'dist/api.js', versions: ['>=3.0.0 <4'] }, exports => {
|
|
14
14
|
const methodNames = Object.getOwnPropertyNames(exports.OpenAIApi.prototype)
|
|
15
15
|
methodNames.shift() // remove leading 'constructor' method
|
|
16
16
|
|