dd-trace 4.38.1 → 4.40.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 (73) hide show
  1. package/LICENSE-3rdparty.csv +0 -3
  2. package/README.md +8 -18
  3. package/ci/init.js +7 -0
  4. package/ext/exporters.d.ts +1 -0
  5. package/ext/exporters.js +2 -1
  6. package/ext/tags.d.ts +1 -0
  7. package/ext/tags.js +1 -0
  8. package/index.d.ts +18 -3
  9. package/initialize.mjs +52 -0
  10. package/package.json +9 -12
  11. package/packages/datadog-instrumentations/src/amqplib.js +5 -2
  12. package/packages/datadog-instrumentations/src/apollo-server-core.js +0 -1
  13. package/packages/datadog-instrumentations/src/apollo-server.js +0 -1
  14. package/packages/datadog-instrumentations/src/body-parser.js +0 -1
  15. package/packages/datadog-instrumentations/src/check_require_cache.js +67 -5
  16. package/packages/datadog-instrumentations/src/cookie-parser.js +0 -1
  17. package/packages/datadog-instrumentations/src/express.js +0 -1
  18. package/packages/datadog-instrumentations/src/graphql.js +0 -2
  19. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  20. package/packages/datadog-instrumentations/src/helpers/register.js +5 -2
  21. package/packages/datadog-instrumentations/src/http/server.js +0 -1
  22. package/packages/datadog-instrumentations/src/jest.js +6 -3
  23. package/packages/datadog-instrumentations/src/mocha/common.js +48 -0
  24. package/packages/datadog-instrumentations/src/mocha/main.js +487 -0
  25. package/packages/datadog-instrumentations/src/mocha/utils.js +306 -0
  26. package/packages/datadog-instrumentations/src/mocha/worker.js +51 -0
  27. package/packages/datadog-instrumentations/src/mocha.js +4 -673
  28. package/packages/datadog-instrumentations/src/openai.js +188 -17
  29. package/packages/datadog-instrumentations/src/playwright.js +4 -3
  30. package/packages/datadog-instrumentations/src/router.js +1 -1
  31. package/packages/datadog-instrumentations/src/selenium.js +13 -6
  32. package/packages/datadog-plugin-graphql/src/resolve.js +4 -0
  33. package/packages/datadog-plugin-mocha/src/index.js +82 -8
  34. package/packages/datadog-plugin-next/src/index.js +1 -2
  35. package/packages/datadog-plugin-openai/src/index.js +219 -73
  36. package/packages/dd-trace/src/appsec/addresses.js +4 -2
  37. package/packages/dd-trace/src/appsec/blocking.js +19 -25
  38. package/packages/dd-trace/src/appsec/channels.js +2 -1
  39. package/packages/dd-trace/src/appsec/graphql.js +10 -3
  40. package/packages/dd-trace/src/appsec/index.js +11 -4
  41. package/packages/dd-trace/src/appsec/rasp.js +35 -0
  42. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +2 -1
  43. package/packages/dd-trace/src/appsec/remote_config/index.js +1 -0
  44. package/packages/dd-trace/src/appsec/rule_manager.js +15 -25
  45. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +2 -5
  46. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +3 -1
  47. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +5 -1
  48. package/packages/dd-trace/src/config.js +97 -22
  49. package/packages/dd-trace/src/constants.js +2 -0
  50. package/packages/dd-trace/src/encode/0.4.js +47 -8
  51. package/packages/dd-trace/src/exporter.js +1 -0
  52. package/packages/dd-trace/src/flare/file.js +44 -0
  53. package/packages/dd-trace/src/flare/index.js +98 -0
  54. package/packages/dd-trace/src/log/channels.js +54 -29
  55. package/packages/dd-trace/src/log/writer.js +7 -49
  56. package/packages/dd-trace/src/opentelemetry/span.js +8 -0
  57. package/packages/dd-trace/src/opentracing/propagation/text_map.js +57 -12
  58. package/packages/dd-trace/src/plugins/index.js +1 -0
  59. package/packages/dd-trace/src/plugins/util/ip_extractor.js +1 -1
  60. package/packages/dd-trace/src/plugins/util/test.js +6 -0
  61. package/packages/dd-trace/src/priority_sampler.js +8 -4
  62. package/packages/dd-trace/src/profiler.js +2 -1
  63. package/packages/dd-trace/src/profiling/config.js +1 -0
  64. package/packages/dd-trace/src/profiling/profiler.js +1 -1
  65. package/packages/dd-trace/src/profiling/{ssi-telemetry.js → ssi-heuristics.js} +64 -36
  66. package/packages/dd-trace/src/profiling/ssi-telemetry-mock-profiler.js +4 -9
  67. package/packages/dd-trace/src/proxy.js +49 -15
  68. package/packages/dd-trace/src/ritm.js +13 -1
  69. package/packages/dd-trace/src/sampling_rule.js +2 -1
  70. package/packages/dd-trace/src/startup-log.js +19 -15
  71. package/packages/dd-trace/src/telemetry/index.js +6 -2
  72. package/packages/dd-trace/src/tracer.js +3 -0
  73. package/packages/dd-trace/src/plugins/util/ip_blocklist.js +0 -51
@@ -0,0 +1,306 @@
1
+ 'use strict'
2
+
3
+ const {
4
+ getTestSuitePath,
5
+ removeEfdStringFromTestName,
6
+ addEfdStringToTestName
7
+ } = require('../../../dd-trace/src/plugins/util/test')
8
+ const { channel, AsyncResource } = require('../helpers/instrument')
9
+ const shimmer = require('../../../datadog-shimmer')
10
+
11
+ // test channels
12
+ const testStartCh = channel('ci:mocha:test:start')
13
+ const testFinishCh = channel('ci:mocha:test:finish')
14
+ const errorCh = channel('ci:mocha:test:error')
15
+ const skipCh = channel('ci:mocha:test:skip')
16
+
17
+ // suite channels
18
+ const testSuiteErrorCh = channel('ci:mocha:test-suite:error')
19
+
20
+ const testToAr = new WeakMap()
21
+ const originalFns = new WeakMap()
22
+ const testToStartLine = new WeakMap()
23
+ const testFileToSuiteAr = new Map()
24
+ const wrappedFunctions = new WeakSet()
25
+
26
+ function isNewTest (test, knownTests) {
27
+ const testSuite = getTestSuitePath(test.file, process.cwd())
28
+ const testName = removeEfdStringFromTestName(test.fullTitle())
29
+ const testsForSuite = knownTests.mocha?.[testSuite] || []
30
+ return !testsForSuite.includes(testName)
31
+ }
32
+
33
+ function retryTest (test, earlyFlakeDetectionNumRetries) {
34
+ const originalTestName = test.title
35
+ const suite = test.parent
36
+ for (let retryIndex = 0; retryIndex < earlyFlakeDetectionNumRetries; retryIndex++) {
37
+ const clonedTest = test.clone()
38
+ clonedTest.title = addEfdStringToTestName(originalTestName, retryIndex + 1)
39
+ suite.addTest(clonedTest)
40
+ clonedTest._ddIsNew = true
41
+ clonedTest._ddIsEfdRetry = true
42
+ }
43
+ }
44
+
45
+ function getSuitesByTestFile (root) {
46
+ const suitesByTestFile = {}
47
+ function getSuites (suite) {
48
+ if (suite.file) {
49
+ if (suitesByTestFile[suite.file]) {
50
+ suitesByTestFile[suite.file].push(suite)
51
+ } else {
52
+ suitesByTestFile[suite.file] = [suite]
53
+ }
54
+ }
55
+ suite.suites.forEach(suite => {
56
+ getSuites(suite)
57
+ })
58
+ }
59
+ getSuites(root)
60
+
61
+ const numSuitesByTestFile = Object.keys(suitesByTestFile).reduce((acc, testFile) => {
62
+ acc[testFile] = suitesByTestFile[testFile].length
63
+ return acc
64
+ }, {})
65
+
66
+ return { suitesByTestFile, numSuitesByTestFile }
67
+ }
68
+
69
+ function isMochaRetry (test) {
70
+ return test._currentRetry !== undefined && test._currentRetry !== 0
71
+ }
72
+
73
+ function getTestFullName (test) {
74
+ return `mocha.${getTestSuitePath(test.file, process.cwd())}.${removeEfdStringFromTestName(test.fullTitle())}`
75
+ }
76
+
77
+ function getTestStatus (test) {
78
+ if (test.isPending()) {
79
+ return 'skip'
80
+ }
81
+ if (test.isFailed() || test.timedOut) {
82
+ return 'fail'
83
+ }
84
+ return 'pass'
85
+ }
86
+
87
+ function getTestAsyncResource (test) {
88
+ if (!test.fn) {
89
+ return testToAr.get(test)
90
+ }
91
+ if (!wrappedFunctions.has(test.fn)) {
92
+ return testToAr.get(test.fn)
93
+ }
94
+ const originalFn = originalFns.get(test.fn)
95
+ return testToAr.get(originalFn)
96
+ }
97
+
98
+ function runnableWrapper (RunnablePackage) {
99
+ shimmer.wrap(RunnablePackage.prototype, 'run', run => function () {
100
+ if (!testStartCh.hasSubscribers) {
101
+ return run.apply(this, arguments)
102
+ }
103
+ const isBeforeEach = this.parent._beforeEach.includes(this)
104
+ const isAfterEach = this.parent._afterEach.includes(this)
105
+
106
+ const isTestHook = isBeforeEach || isAfterEach
107
+
108
+ // we restore the original user defined function
109
+ if (wrappedFunctions.has(this.fn)) {
110
+ const originalFn = originalFns.get(this.fn)
111
+ this.fn = originalFn
112
+ wrappedFunctions.delete(this.fn)
113
+ }
114
+
115
+ if (isTestHook || this.type === 'test') {
116
+ const test = isTestHook ? this.ctx.currentTest : this
117
+ const asyncResource = getTestAsyncResource(test)
118
+
119
+ if (asyncResource) {
120
+ // we bind the test fn to the correct async resource
121
+ const newFn = asyncResource.bind(this.fn)
122
+
123
+ // we store the original function, not to lose it
124
+ originalFns.set(newFn, this.fn)
125
+ this.fn = newFn
126
+
127
+ wrappedFunctions.add(this.fn)
128
+ }
129
+ }
130
+
131
+ return run.apply(this, arguments)
132
+ })
133
+ return RunnablePackage
134
+ }
135
+
136
+ function getOnTestHandler (isMain, newTests) {
137
+ return function (test) {
138
+ if (isMochaRetry(test)) {
139
+ return
140
+ }
141
+ const testStartLine = testToStartLine.get(test)
142
+ const asyncResource = new AsyncResource('bound-anonymous-fn')
143
+ testToAr.set(test.fn, asyncResource)
144
+
145
+ const {
146
+ file: testSuiteAbsolutePath,
147
+ title,
148
+ _ddIsNew: isNew,
149
+ _ddIsEfdRetry: isEfdRetry
150
+ } = test
151
+
152
+ const testInfo = {
153
+ testName: test.fullTitle(),
154
+ testSuiteAbsolutePath,
155
+ title,
156
+ testStartLine
157
+ }
158
+
159
+ if (isMain) {
160
+ testInfo.isNew = isNew
161
+ testInfo.isEfdRetry = isEfdRetry
162
+ // We want to store the result of the new tests
163
+ if (isNew) {
164
+ const testFullName = getTestFullName(test)
165
+ if (newTests[testFullName]) {
166
+ newTests[testFullName].push(test)
167
+ } else {
168
+ newTests[testFullName] = [test]
169
+ }
170
+ }
171
+ } else {
172
+ testInfo.isParallel = true
173
+ }
174
+
175
+ asyncResource.runInAsyncScope(() => {
176
+ testStartCh.publish(testInfo)
177
+ })
178
+ }
179
+ }
180
+
181
+ function getOnTestEndHandler () {
182
+ return function (test) {
183
+ const asyncResource = getTestAsyncResource(test)
184
+ const status = getTestStatus(test)
185
+
186
+ // if there are afterEach to be run, we don't finish the test yet
187
+ if (asyncResource && !test.parent._afterEach.length) {
188
+ asyncResource.runInAsyncScope(() => {
189
+ testFinishCh.publish(status)
190
+ })
191
+ }
192
+ }
193
+ }
194
+
195
+ function getOnHookEndHandler () {
196
+ return function (hook) {
197
+ const test = hook.ctx.currentTest
198
+ if (test && hook.parent._afterEach.includes(hook)) { // only if it's an afterEach
199
+ const isLastAfterEach = hook.parent._afterEach.indexOf(hook) === hook.parent._afterEach.length - 1
200
+ if (isLastAfterEach) {
201
+ const status = getTestStatus(test)
202
+ const asyncResource = getTestAsyncResource(test)
203
+ asyncResource.runInAsyncScope(() => {
204
+ testFinishCh.publish(status)
205
+ })
206
+ }
207
+ }
208
+ }
209
+ }
210
+
211
+ function getOnFailHandler (isMain) {
212
+ return function (testOrHook, err) {
213
+ const testFile = testOrHook.file
214
+ let test = testOrHook
215
+ const isHook = testOrHook.type === 'hook'
216
+ if (isHook && testOrHook.ctx) {
217
+ test = testOrHook.ctx.currentTest
218
+ }
219
+ let testAsyncResource
220
+ if (test) {
221
+ testAsyncResource = getTestAsyncResource(test)
222
+ }
223
+ if (testAsyncResource) {
224
+ testAsyncResource.runInAsyncScope(() => {
225
+ if (isHook) {
226
+ err.message = `${testOrHook.fullTitle()}: ${err.message}`
227
+ errorCh.publish(err)
228
+ // if it's a hook and it has failed, 'test end' will not be called
229
+ testFinishCh.publish('fail')
230
+ } else {
231
+ errorCh.publish(err)
232
+ }
233
+ })
234
+ }
235
+
236
+ if (isMain) {
237
+ const testSuiteAsyncResource = testFileToSuiteAr.get(testFile)
238
+
239
+ if (testSuiteAsyncResource) {
240
+ // we propagate the error to the suite
241
+ const testSuiteError = new Error(
242
+ `"${testOrHook.parent.fullTitle()}" failed with message "${err.message}"`
243
+ )
244
+ testSuiteError.stack = err.stack
245
+ testSuiteAsyncResource.runInAsyncScope(() => {
246
+ testSuiteErrorCh.publish(testSuiteError)
247
+ })
248
+ }
249
+ }
250
+ }
251
+ }
252
+
253
+ function getOnPendingHandler () {
254
+ return function (test) {
255
+ const testStartLine = testToStartLine.get(test)
256
+ const {
257
+ file: testSuiteAbsolutePath,
258
+ title
259
+ } = test
260
+
261
+ const testInfo = {
262
+ testName: test.fullTitle(),
263
+ testSuiteAbsolutePath,
264
+ title,
265
+ testStartLine
266
+ }
267
+
268
+ const asyncResource = getTestAsyncResource(test)
269
+ if (asyncResource) {
270
+ asyncResource.runInAsyncScope(() => {
271
+ skipCh.publish(testInfo)
272
+ })
273
+ } else {
274
+ // if there is no async resource, the test has been skipped through `test.skip`
275
+ // or the parent suite is skipped
276
+ const skippedTestAsyncResource = new AsyncResource('bound-anonymous-fn')
277
+ if (test.fn) {
278
+ testToAr.set(test.fn, skippedTestAsyncResource)
279
+ } else {
280
+ testToAr.set(test, skippedTestAsyncResource)
281
+ }
282
+ skippedTestAsyncResource.runInAsyncScope(() => {
283
+ skipCh.publish(testInfo)
284
+ })
285
+ }
286
+ }
287
+ }
288
+ module.exports = {
289
+ isNewTest,
290
+ retryTest,
291
+ getSuitesByTestFile,
292
+ isMochaRetry,
293
+ getTestFullName,
294
+ getTestStatus,
295
+ runnableWrapper,
296
+ testToAr,
297
+ originalFns,
298
+ getTestAsyncResource,
299
+ testToStartLine,
300
+ getOnTestHandler,
301
+ getOnTestEndHandler,
302
+ getOnHookEndHandler,
303
+ getOnFailHandler,
304
+ getOnPendingHandler,
305
+ testFileToSuiteAr
306
+ }
@@ -0,0 +1,51 @@
1
+ 'use strict'
2
+
3
+ const { addHook, channel } = require('../helpers/instrument')
4
+ const shimmer = require('../../../datadog-shimmer')
5
+
6
+ const {
7
+ runnableWrapper,
8
+ getOnTestHandler,
9
+ getOnTestEndHandler,
10
+ getOnHookEndHandler,
11
+ getOnFailHandler,
12
+ getOnPendingHandler
13
+ } = require('./utils')
14
+ require('./common')
15
+
16
+ const workerFinishCh = channel('ci:mocha:worker:finish')
17
+
18
+ // Runner is also hooked in mocha/main.js, but in here we only generate test events.
19
+ addHook({
20
+ name: 'mocha',
21
+ versions: ['>=5.2.0'],
22
+ file: 'lib/runner.js'
23
+ }, function (Runner) {
24
+ shimmer.wrap(Runner.prototype, 'run', run => function () {
25
+ // We flush when the worker ends with its test file (a mocha instance in a worker runs a single test file)
26
+ this.on('end', () => {
27
+ workerFinishCh.publish()
28
+ })
29
+ this.on('test', getOnTestHandler(false))
30
+
31
+ this.on('test end', getOnTestEndHandler())
32
+
33
+ // If the hook passes, 'hook end' will be emitted. Otherwise, 'fail' will be emitted
34
+ this.on('hook end', getOnHookEndHandler())
35
+
36
+ this.on('fail', getOnFailHandler(false))
37
+
38
+ this.on('pending', getOnPendingHandler())
39
+
40
+ return run.apply(this, arguments)
41
+ })
42
+ return Runner
43
+ })
44
+
45
+ // Used both in serial and parallel mode, and by both the main process and the workers
46
+ // Used to set the correct async resource to the test.
47
+ addHook({
48
+ name: 'mocha',
49
+ versions: ['>=5.2.0'],
50
+ file: 'lib/runnable.js'
51
+ }, runnableWrapper)