dd-trace 5.0.0-pre-8a512a2 → 5.0.0-pre-244a0ea

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 (65) hide show
  1. package/CONTRIBUTING.md +66 -0
  2. package/README.md +4 -9
  3. package/index.d.ts +43 -0
  4. package/package.json +4 -3
  5. package/packages/datadog-esbuild/index.js +29 -9
  6. package/packages/datadog-instrumentations/src/couchbase.js +41 -5
  7. package/packages/datadog-instrumentations/src/cucumber.js +30 -11
  8. package/packages/datadog-instrumentations/src/http2/client.js +2 -0
  9. package/packages/datadog-instrumentations/src/jest.js +22 -11
  10. package/packages/datadog-instrumentations/src/mocha.js +30 -8
  11. package/packages/datadog-instrumentations/src/next.js +102 -16
  12. package/packages/datadog-instrumentations/src/openai.js +1 -1
  13. package/packages/datadog-instrumentations/src/pg.js +46 -0
  14. package/packages/datadog-plugin-aws-sdk/src/base.js +22 -10
  15. package/packages/datadog-plugin-aws-sdk/src/services/lambda.js +16 -0
  16. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +18 -0
  17. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +23 -0
  18. package/packages/datadog-plugin-couchbase/src/index.js +32 -30
  19. package/packages/datadog-plugin-cucumber/src/index.js +14 -2
  20. package/packages/datadog-plugin-cypress/src/plugin.js +17 -8
  21. package/packages/datadog-plugin-graphql/src/index.js +3 -3
  22. package/packages/datadog-plugin-http/src/client.js +14 -23
  23. package/packages/datadog-plugin-http/src/server.js +10 -2
  24. package/packages/datadog-plugin-http2/src/client.js +2 -16
  25. package/packages/datadog-plugin-http2/src/server.js +10 -1
  26. package/packages/datadog-plugin-jest/src/index.js +10 -2
  27. package/packages/datadog-plugin-jest/src/util.js +10 -4
  28. package/packages/datadog-plugin-mocha/src/index.js +14 -2
  29. package/packages/datadog-plugin-next/src/index.js +13 -5
  30. package/packages/datadog-plugin-pg/src/index.js +1 -1
  31. package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +19 -4
  32. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +1 -1
  33. package/packages/dd-trace/src/appsec/iast/telemetry/index.js +14 -5
  34. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +120 -10
  35. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +0 -1
  36. package/packages/dd-trace/src/appsec/remote_config/index.js +2 -0
  37. package/packages/dd-trace/src/appsec/remote_config/manager.js +9 -4
  38. package/packages/dd-trace/src/config.js +138 -16
  39. package/packages/dd-trace/src/dogstatsd.js +65 -5
  40. package/packages/dd-trace/src/exporters/agent/writer.js +9 -9
  41. package/packages/dd-trace/src/exporters/common/request.js +13 -4
  42. package/packages/dd-trace/src/noop/tracer.js +2 -0
  43. package/packages/dd-trace/src/opentracing/span.js +13 -13
  44. package/packages/dd-trace/src/opentracing/tracer.js +3 -3
  45. package/packages/dd-trace/src/plugin_manager.js +2 -0
  46. package/packages/dd-trace/src/plugins/ci_plugin.js +22 -1
  47. package/packages/dd-trace/src/plugins/database.js +12 -0
  48. package/packages/dd-trace/src/plugins/util/test.js +18 -1
  49. package/packages/dd-trace/src/plugins/util/web.js +6 -4
  50. package/packages/dd-trace/src/priority_sampler.js +6 -3
  51. package/packages/dd-trace/src/profiling/config.js +3 -1
  52. package/packages/dd-trace/src/profiling/profilers/wall.js +23 -7
  53. package/packages/dd-trace/src/proxy.js +41 -5
  54. package/packages/dd-trace/src/ritm.js +10 -2
  55. package/packages/dd-trace/src/service-naming/schemas/util.js +19 -1
  56. package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +13 -1
  57. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +4 -0
  58. package/packages/dd-trace/src/service-naming/schemas/v0/web.js +33 -1
  59. package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +14 -2
  60. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +4 -0
  61. package/packages/dd-trace/src/service-naming/schemas/v1/web.js +33 -1
  62. package/packages/dd-trace/src/startup-log.js +1 -1
  63. package/packages/dd-trace/src/telemetry/index.js +31 -3
  64. package/packages/dd-trace/src/tracer.js +4 -0
  65. /package/packages/dd-trace/src/{metrics.js → runtime_metrics.js} +0 -0
package/CONTRIBUTING.md CHANGED
@@ -4,4 +4,70 @@ Please reach out before starting work on any major code changes.
4
4
  This will ensure we avoid duplicating work, or that your code can't be merged due to a rapidly changing
5
5
  base. If you would like support for a module that is not listed, [contact support][1] to share a request.
6
6
 
7
+ ## Keep changes small and incremental
8
+
9
+ Changes should be incremental and understandable. As much as possible, large-scale efforts should be broken up into many PRs over time for better reviewability. If a feature would require more changes to be "complete" it's fine to land partial changes if they are not wired up to anything yet, so long as tests are included which at least prove those parts work in isolation.
10
+
11
+ There are great benefits to taking a measured and iterative approach to improvement. When working on code in fewer places there is far less risk of running into merge conflicts or incompatibilities with other systems. Keeping contributions small makes them easy to review which makes that much quicker to land. Additionally, keeping things small and iterative makes it easier for other teams to review and understand what the code does.
12
+
13
+ ## Be descriptive
14
+
15
+ Sometimes code can be self-documenting, but often it can't. That is especially true to someone reviewing code they haven't worked on. Be conscious of writing code in a self-describing way and leave comments anywhere that self-description fails. This goes a long way towards making even complex code coherent to one not already familiar with it.
16
+
17
+ Try to write code in a way the describes the intent when read. For example, verbs can be used for function and method names to communicate that they are used to do some specific action. In doing so it becomes clear when referenced by name elsewhere that it is a function and what the function is meant to do. If a function can not be described with a simple verb it's probably too complex or does too many things.
18
+
19
+ ## Give your code space
20
+
21
+ Very dense code is hard to read. It helps to make use of empty lines to separate logical groupings of statements. Long lines should be split up into multiple lines to make them more readable. Complex objects or arrays should generally be split over several lines. Sometimes it's a good idea to assign a variable only to immediately use it in a call as it can be more descriptive than just using the expression in place. It's not always clear what an argument is for if it doesn't visibly have a name somehow. Remember, lines are free, our time is not.
22
+
23
+ ## Avoid large refactors
24
+
25
+ Large refactors should generally be avoided in favour of iterative approaches. For example, rather than rewriting how every plugin works, one might make a special-case plugin that works a bit different for their particular use-case. If several dozen files need to change to add a feature we've probably done something wrong.
26
+
27
+ Sometimes new patterns or new ideas emerge which would be a substantial improvement over the existing state. It can be tempting to want to go all-in on a new way to do something, but the code churn can be hard to manage. It's best to introduce such new things incrementally and advocate for their adoption gradually through the rest of the codebase. As old systems are gradually phased out, the infrastructure which supports them can be deleted or relegated to lazy-loading only if and when that specific part of the system needs to be used.
28
+
29
+ ## Test everything
30
+
31
+ It's very difficult to know if a change is valid unless there are tests to prove it. As an extension of that, it's also difficult to know the _use_ of that code is valid if the way it is integrated is not propertly tested. For this reason we generally favour integration tests over unit tests. If an API is expected to be used in different places or in different ways then it should generally include unit tests too for each unique scenario, however great care should be taken to ensure unit tests are actually testing the _logic_ and not just testing the _mocks_. It's a very common mistake to write a unit test that abstracts away the actual use of the interface so much that it doesn't actually test how that interface works in real-world scenarios. Remember to test how it handles failures, how it operates under heavy load, and how it impacts usability of what its purpose is.
32
+
33
+ ## Don't forget benchmarks
34
+
35
+ Observability products tend to have quite a bit of their behaviour running in app code hot paths. It's important we extensively benchmark anything we expect to have heavy use to ensure it performs well and we don't cause any significant regressions through future changes. Measuring once at the time of writing is insufficient--a graph with just one data point is not going to tell you much of anything.
36
+
37
+ ## Always consider backportability
38
+
39
+ To reduce delta between release lines and make it easier for us to support older versions we try as much as possible to backport every change we can. We should be diligent about keeping breaking changes to a minimum and ensuring we don't use language or runtime features which are too new. This way we can generally be confident that a change can be backported.
40
+
41
+ To reduce the surface area of a breaking change, the breaking aspects could be placed behind a flag which is disabled by default or isolated to a function. In the next major the change would then be just to change the default of the flag or to start or stop calling the isolated function. By isolating the breaking logic it also becomes easier to delete later when it's no longer relevant on any release line.
42
+
43
+ Currently we do not have CI to test PRs for mergeability to past release lines, but we intend to expand our CI to include that in the future. For the time being, it's recommended when developing locally to try to cherry-pick your changes onto the previous vN.x branches to see if the tests pass there too.
44
+
45
+ ## Respect semantic versioning
46
+
47
+ This library follows the semantic versioning standard, but there are some subtleties left under-specified so this section is meant to clarify exactly how we interpret the meaning of semver. Additionally, it exists to communicate that we also use semver labels on all PRs to indicate which type of release the change should land in. Outside contributions should be evaluated and a semver label selected by the relevant team.
48
+
49
+ ### semver-patch
50
+
51
+ If the change is a bug or security fix, it should be labelled as semver-patch. These changes should generally not alter existing behaviour in any way other than to correct the specific issue.
52
+
53
+ ### semver-minor
54
+
55
+ Any addition of new functionality should be labelled as semver-minor and should not change any existing behaviour either in how any existing API works or in changing the contents or value of any existing data being reported except in purely additive cases where all existing data retains its prior state. Such changes may include new configuration options which when used will change behaviour, or may include the addition of new data being captured such as a new instrumentation, but should not impact the current operating design of any existing features.
56
+
57
+ ### semver-major
58
+
59
+ In the event that some existing functionality _does_ need to change, as much as possible the non-breaking aspects of that change should be made in a semver-minor PR and the actually breaking aspects should be done via a follow-up PR with only the specific aspects which are breaking. Remember to [always consider backportability](#always-consider-backportability).
60
+
61
+ ## Indicate intended release targets
62
+
63
+ When writing major changes we use a series of labels in the form of `dont-land-on-vN.x` where N is the major release line which a PR should not land in. Every PR marked as semver-major should include these tags. These tags allow our [branch-diff](https://github.com/bengl/branch-diff) tooling to work smoothly as we can exclude PRs not intended for the release line we're preparing a release proposal for. The `semver-major` labels on their own are not sufficient as they don't encode any indication of from _which_ releases they are a major change.
64
+
65
+ For outside contributions we will have the relevant team add these labels when they review and determine when they plan to release it.
66
+
67
+ ## Ensure all tests are green
68
+
69
+ We follow an all-green policy which means that for any PR to be merged _all_ tests must be passing. If a test is flaky or failing consistently the owner of that test should make it a priority to fix that test and unblock other teams from landing changes. For outside contributors there are currently several tests which will always fail as full CI permission is required. For these PRs our current process is for the relevant team to copy the PR and resubmit it to run tests as a user with full CI permission.
70
+
71
+ Eventually we plan to look into putting these permission-required tests behind a label which team members can add to their PRs at creation to run the full CI and can add to outside contributor PRs to trigger the CI from their own user credentials. If the label is not present there will be another action which checks the label is present. Rather than showing a bunch of confusing failures to new contributors it would just show a single job failure which indicates an additional label is required, and we can name it in a way that makes it clear that it's not the responsibility of the outside contributor to add it. Something like `approve-full-ci` is one possible choice there.
72
+
7
73
  [1]: https://docs.datadoghq.com/help
package/README.md CHANGED
@@ -2,8 +2,6 @@
2
2
 
3
3
  [![npm v4](https://img.shields.io/npm/v/dd-trace/latest?color=blue&label=dd-trace%40v4&logo=npm)](https://www.npmjs.com/package/dd-trace)
4
4
  [![npm v3](https://img.shields.io/npm/v/dd-trace/latest-node14?color=blue&label=dd-trace%40v3&logo=npm)](https://www.npmjs.com/package/dd-trace/v/latest-node12)
5
- [![npm v2](https://img.shields.io/npm/v/dd-trace/latest-node12?color=blue&label=dd-trace%40v2&logo=npm)](https://www.npmjs.com/package/dd-trace/v/latest-node12)
6
- [![npm dev](https://img.shields.io/npm/v/dd-trace/dev?color=orange&label=dd-trace%40dev&logo=npm)](https://www.npmjs.com/package/dd-trace/v/dev)
7
5
  [![codecov](https://codecov.io/gh/DataDog/dd-trace-js/branch/master/graph/badge.svg)](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) | ![npm v1](https://img.shields.io/npm/v/dd-trace/legacy-v1?color=white&label=%20&style=flat-square) | `>= v12` | **End of Life** | 2021-07-13 | 2022-02-25 |
31
- | [`v2`](https://github.com/DataDog/dd-trace-js/tree/v2.x) | ![npm v2](https://img.shields.io/npm/v/dd-trace/latest-node12?color=white&label=%20&style=flat-square) | `>= v12` | **Maintenance** | 2022-01-28 | 2023-08-15 |
29
+ | [`v2`](https://github.com/DataDog/dd-trace-js/tree/v2.x) | ![npm v2](https://img.shields.io/npm/v/dd-trace/latest-node12?color=white&label=%20&style=flat-square) | `>= v12` | **End of Life** | 2022-01-28 | 2023-08-15 |
32
30
  | [`v3`](https://github.com/DataDog/dd-trace-js/tree/v3.x) | ![npm v3](https://img.shields.io/npm/v/dd-trace/latest-node14?color=white&label=%20&style=flat-square) | `>= v14` | **Maintenance** | 2022-08-15 | 2024-05-15 |
33
31
  | [`v4`](https://github.com/DataDog/dd-trace-js/tree/v4.x) | ![npm v4](https://img.shields.io/npm/v/dd-trace/latest?color=white&label=%20&style=flat-square) | `>= v16` | **Current** | 2023-05-12 | Unknown |
34
32
 
35
- We currently maintain three release lines, namely `v2`, `v3` and `v4`.
36
- Features and bug fixes that are merged are released to the `v4` line and, if appropriate, also the `v2` and `v3` line.
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 `v2` or `v3` release lines, or projects that need to support EOL versions of Node.js, may continue to use these release lines.
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": "5.0.0-pre-8a512a2",
3
+ "version": "5.0.0-pre-244a0ea",
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,14 +71,14 @@
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.1.0",
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",
77
78
  "crypto-randomuuid": "^1.0.0",
78
79
  "diagnostics_channel": "^1.1.0",
79
80
  "ignore": "^5.2.4",
80
- "import-in-the-middle": "^1.4.1",
81
+ "import-in-the-middle": "^1.4.2",
81
82
  "int64-buffer": "^0.1.9",
82
83
  "ipaddr.js": "^2.1.0",
83
84
  "istanbul-lib-coverage": "3.2.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(packageName) || modulesOfInterest.has(`${extracted.pkg}/${extracted.path}`))
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 ${packageName}@${packageJson.version}`)
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: packageName,
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(packageName)) return
125
+ if (builtins.has(args.path)) return
106
126
 
107
127
  return {
108
- path: require.resolve(packageName, { paths: [ args.resolveDir ] }),
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}`
@@ -74,7 +74,7 @@ function wrap (prefix, fn) {
74
74
  return asyncResource.runInAsyncScope(() => {
75
75
  const cb = callbackResource.bind(arguments[callbackIndex])
76
76
 
77
- startCh.publish({ bucket: { name: this.name || this._name } })
77
+ startCh.publish({ bucket: { name: this.name || this._name }, seedNodes: this._dd_hosts })
78
78
 
79
79
  arguments[callbackIndex] = asyncResource.bind(function (error, result) {
80
80
  if (error) {
@@ -146,7 +146,8 @@ function wrapWithName (name) {
146
146
  return function () { // no arguments used by us
147
147
  return wrapCBandPromise(operation, name, {
148
148
  collection: { name: this._name || '_default' },
149
- bucket: { name: this._scope._bucket._name }
149
+ bucket: { name: this._scope._bucket._name },
150
+ seedNodes: this._dd_connStr
150
151
  }, this, arguments)
151
152
  }
152
153
  }
@@ -155,7 +156,7 @@ function wrapWithName (name) {
155
156
  function wrapV3Query (query) {
156
157
  return function (q) {
157
158
  const resource = getQueryResource(q)
158
- return wrapCBandPromise(query, 'query', { resource }, this, arguments)
159
+ return wrapCBandPromise(query, 'query', { resource, seedNodes: this._connStr }, this, arguments)
159
160
  }
160
161
  }
161
162
 
@@ -179,7 +180,7 @@ addHook({ name: 'couchbase', file: 'lib/bucket.js', versions: ['^2.6.12'] }, Buc
179
180
 
180
181
  const asyncResource = new AsyncResource('bound-anonymous-fn')
181
182
  return asyncResource.runInAsyncScope(() => {
182
- startCh.publish({ resource: n1qlQuery, bucket: { name: this.name || this._name } })
183
+ startCh.publish({ resource: n1qlQuery, bucket: { name: this.name || this._name }, seedNodes: this._dd_hosts })
183
184
 
184
185
  emitter.once('rows', asyncResource.bind(() => {
185
186
  finishCh.publish(undefined)
@@ -212,11 +213,32 @@ addHook({ name: 'couchbase', file: 'lib/cluster.js', versions: ['^2.6.12'] }, Cl
212
213
  Cluster.prototype._maybeInvoke = wrapMaybeInvoke(Cluster.prototype._maybeInvoke)
213
214
  Cluster.prototype.query = wrapQuery(Cluster.prototype.query)
214
215
 
216
+ shimmer.wrap(Cluster.prototype, 'openBucket', openBucket => {
217
+ return function () {
218
+ const bucket = openBucket.apply(this, arguments)
219
+ const hosts = this.dsnObj.hosts
220
+ bucket._dd_hosts = hosts.map(hostAndPort => hostAndPort.join(':')).join(',')
221
+ return bucket
222
+ }
223
+ })
215
224
  return Cluster
216
225
  })
217
226
 
218
227
  // semver >=3 <3.2.0
219
228
 
229
+ addHook({ name: 'couchbase', file: 'lib/bucket.js', versions: ['^3.0.7', '^3.1.3'] }, Bucket => {
230
+ shimmer.wrap(Bucket.prototype, 'collection', getCollection => {
231
+ return function () {
232
+ const collection = getCollection.apply(this, arguments)
233
+ const connStr = this._cluster._connStr
234
+ collection._dd_connStr = connStr
235
+ return collection
236
+ }
237
+ })
238
+
239
+ return Bucket
240
+ })
241
+
220
242
  addHook({ name: 'couchbase', file: 'lib/collection.js', versions: ['^3.0.7', '^3.1.3'] }, Collection => {
221
243
  wrapAllNames(['upsert', 'insert', 'replace'], name => {
222
244
  shimmer.wrap(Collection.prototype, name, wrapWithName(name))
@@ -242,7 +264,21 @@ addHook({ name: 'couchbase', file: 'dist/collection.js', versions: ['>=3.2.0'] }
242
264
  return collection
243
265
  })
244
266
 
245
- addHook({ name: 'couchbase', file: 'dist/cluster.js', versions: ['3.2.0 - 3.2.1', '>=3.2.2'] }, cluster => {
267
+ addHook({ name: 'couchbase', file: 'dist/bucket.js', versions: ['>=3.2.0'] }, bucket => {
268
+ const Bucket = bucket.Bucket
269
+ shimmer.wrap(Bucket.prototype, 'collection', getCollection => {
270
+ return function () {
271
+ const collection = getCollection.apply(this, arguments)
272
+ const connStr = this._cluster._connStr
273
+ collection._dd_connStr = connStr
274
+ return collection
275
+ }
276
+ })
277
+
278
+ return bucket
279
+ })
280
+
281
+ addHook({ name: 'couchbase', file: 'dist/cluster.js', versions: ['3.2.0 - 3.2.1', '>=3.2.2'] }, (cluster) => {
246
282
  const Cluster = cluster.Cluster
247
283
 
248
284
  shimmer.wrap(Cluster.prototype, 'query', wrapV3Query)
@@ -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 getPicklesToRun (runtime, suitesToSkip) {
220
- return runtime.pickleIds.filter((pickleId) => {
220
+ function getFilteredPickles (runtime, suitesToSkip) {
221
+ return runtime.pickleIds.reduce((acc, pickleId) => {
221
222
  const test = runtime.eventDataCollector.getPickle(pickleId)
222
- return !suitesToSkip.includes(getTestSuitePath(test.uri, process.cwd()))
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, cucumberVersion) => {
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 newPickleIds = getPicklesToRun(this, skippableSuites)
269
- isSuitesSkipped = newPickleIds.length !== this.pickleIds.length
270
- this.pickleIds = newPickleIds
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: cucumberVersion })
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
@@ -30,6 +30,8 @@ function createWrapEmit (ctx) {
30
30
  function createWrapRequest (authority, options) {
31
31
  return function wrapRequest (request) {
32
32
  return function (headers) {
33
+ if (!startChannel.hasSubscribers) return request.apply(this, arguments)
34
+
33
35
  const ctx = { headers, authority, options }
34
36
 
35
37
  return startChannel.runStores(ctx, () => {
@@ -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 filteredTests = getJestSuitesToRun(skippableSuites, shardedTests, rootDir || process.cwd())
208
+ const { skippedSuites, suitesToRun } = getJestSuitesToRun(skippableSuites, shardedTests, rootDir || process.cwd())
206
209
 
207
- isSuitesSkipped = filteredTests.length !== shardedTests.length
210
+ isSuitesSkipped = suitesToRun.length !== shardedTests.length
211
+ numSkippedSuites = skippedSuites.length
208
212
 
209
- skippableSuites = []
213
+ itrSkippedSuitesCh.publish({ skippedSuites, frameworkVersion })
210
214
 
211
- return filteredTests
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 filteredTests = getJestSuitesToRun(skippableSuites, tests, rootDir)
503
+ const { skippedSuites, suitesToRun } = getJestSuitesToRun(skippableSuites, tests, rootDir)
504
+
505
+ isSuitesSkipped = suitesToRun.length !== tests.length
506
+ numSkippedSuites = skippedSuites.length
496
507
 
497
- isSuitesSkipped = filteredTests.length !== tests.length
508
+ itrSkippedSuitesCh.publish({ skippedSuites, frameworkVersion })
498
509
 
499
510
  skippableSuites = []
500
511
 
501
- return { ...testPaths, tests: filteredTests }
512
+ return { ...testPaths, tests: suitesToRun }
502
513
  })
503
514
 
504
515
  return searchSourcePackage