dd-trace 5.88.0 → 5.89.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 (59) hide show
  1. package/ext/tags.js +2 -0
  2. package/index.d.ts +9 -0
  3. package/package.json +12 -8
  4. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +26 -111
  5. package/packages/datadog-instrumentations/src/helpers/rewriter/{compiler.js → orchestrion/compiler.js} +5 -5
  6. package/packages/datadog-instrumentations/src/helpers/rewriter/orchestrion/index.js +43 -0
  7. package/packages/datadog-instrumentations/src/helpers/rewriter/orchestrion/matcher.js +49 -0
  8. package/packages/datadog-instrumentations/src/helpers/rewriter/orchestrion/transformer.js +121 -0
  9. package/packages/datadog-instrumentations/src/helpers/rewriter/{transforms.js → orchestrion/transforms.js} +6 -6
  10. package/packages/datadog-instrumentations/src/jest.js +101 -43
  11. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +36 -5
  12. package/packages/datadog-plugin-cypress/src/source-map-utils.js +297 -0
  13. package/packages/datadog-plugin-cypress/src/support.js +4 -1
  14. package/packages/dd-trace/src/aiguard/sdk.js +5 -1
  15. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +1 -0
  16. package/packages/dd-trace/src/config/index.js +2 -0
  17. package/packages/dd-trace/src/config/supported-configurations.json +10 -0
  18. package/packages/dd-trace/src/datastreams/checkpointer.js +13 -0
  19. package/packages/dd-trace/src/datastreams/index.js +3 -0
  20. package/packages/dd-trace/src/datastreams/manager.js +9 -0
  21. package/packages/dd-trace/src/datastreams/processor.js +126 -3
  22. package/packages/dd-trace/src/encode/agentless-json.js +16 -2
  23. package/packages/dd-trace/src/exporters/agent/writer.js +7 -8
  24. package/packages/dd-trace/src/pkg.js +1 -1
  25. package/packages/dd-trace/src/proxy.js +2 -1
  26. package/packages/dd-trace/src/startup-log.js +52 -18
  27. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  28. package/vendor/dist/@datadog/source-map/index.js +1 -1
  29. package/vendor/dist/@isaacs/ttlcache/index.js +1 -1
  30. package/vendor/dist/@opentelemetry/core/index.js +1 -1
  31. package/vendor/dist/@opentelemetry/resources/index.js +1 -1
  32. package/vendor/dist/astring/index.js +1 -1
  33. package/vendor/dist/crypto-randomuuid/index.js +1 -1
  34. package/vendor/dist/escape-string-regexp/index.js +1 -1
  35. package/vendor/dist/esquery/index.js +1 -1
  36. package/vendor/dist/ignore/index.js +1 -1
  37. package/vendor/dist/istanbul-lib-coverage/index.js +1 -1
  38. package/vendor/dist/jest-docblock/index.js +1 -1
  39. package/vendor/dist/jsonpath-plus/index.js +1 -1
  40. package/vendor/dist/limiter/index.js +1 -1
  41. package/vendor/dist/lodash.sortby/index.js +1 -1
  42. package/vendor/dist/lru-cache/index.js +1 -1
  43. package/vendor/dist/meriyah/index.js +1 -1
  44. package/vendor/dist/module-details-from-path/index.js +1 -1
  45. package/vendor/dist/mutexify/promise/index.js +1 -1
  46. package/vendor/dist/opentracing/index.js +1 -1
  47. package/vendor/dist/path-to-regexp/index.js +1 -1
  48. package/vendor/dist/pprof-format/index.js +1 -1
  49. package/vendor/dist/protobufjs/index.js +1 -1
  50. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  51. package/vendor/dist/retry/index.js +1 -1
  52. package/vendor/dist/rfdc/index.js +1 -1
  53. package/vendor/dist/semifies/index.js +1 -1
  54. package/vendor/dist/shell-quote/index.js +1 -1
  55. package/vendor/dist/source-map/index.js +1 -1
  56. package/vendor/dist/source-map/lib/util/index.js +1 -1
  57. package/vendor/dist/tlhunter-sorted-set/index.js +1 -1
  58. package/vendor/dist/ttl-set/index.js +1 -1
  59. package/packages/datadog-instrumentations/src/helpers/rewriter/transformer.js +0 -21
package/ext/tags.js CHANGED
@@ -30,6 +30,8 @@ const tags = {
30
30
 
31
31
  // DSM Specific
32
32
  PATHWAY_HASH: 'pathway.hash',
33
+ DSM_TRANSACTION_ID: 'dsm.transaction.id',
34
+ DSM_TRANSACTION_CHECKPOINT: 'dsm.transaction.checkpoint',
33
35
  }
34
36
 
35
37
  // Deprecated
package/index.d.ts CHANGED
@@ -1339,6 +1339,15 @@ declare namespace tracer {
1339
1339
  * @returns The DSM context associated with the current pathway.
1340
1340
  */
1341
1341
  setConsumeCheckpoint (type: string, source: string, carrier: any, manualCheckpoint?: boolean): any;
1342
+
1343
+ /**
1344
+ * Records a transaction ID at a named checkpoint without pathway propagation.
1345
+ * Tags the active span (or the provided span) with dsm.transaction.id and dsm.transaction.checkpoint.
1346
+ * @param transactionId The unique transaction identifier (truncated to 255 UTF-8 bytes).
1347
+ * @param checkpointName The logical checkpoint name (stable 1-byte ID per process lifetime).
1348
+ * @param span The span to tag. Defaults to the currently active span.
1349
+ */
1350
+ trackTransaction(transactionId: string, checkpointName: string, span?: Span | null): void;
1342
1351
  }
1343
1352
 
1344
1353
  export interface EventTrackingV2 {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dd-trace",
3
- "version": "5.88.0",
3
+ "version": "5.89.0",
4
4
  "description": "Datadog APM tracing client for JavaScript",
5
5
  "main": "index.js",
6
6
  "typings": "index.d.ts",
@@ -16,7 +16,8 @@
16
16
  "lint": "node scripts/check_licenses.js && node scripts/check-no-coverage-artifacts.js && eslint . --concurrency=auto --max-warnings 0",
17
17
  "lint:fix": "node scripts/check_licenses.js && node scripts/check-no-coverage-artifacts.js && eslint . --concurrency=auto --max-warnings 0 --fix",
18
18
  "lint:inspect": "npx @eslint/config-inspector@latest",
19
- "lint:codeowners": "node scripts/codeowners.mjs",
19
+ "lint:codeowners": "codeowners-audit",
20
+ "lint:codeowners:ci": "codeowners-audit --glob='**/*.spec.js'",
20
21
  "release:proposal": "node scripts/release/proposal",
21
22
  "services": "node ./scripts/install_plugin_modules && node packages/dd-trace/test/setup/services",
22
23
  "test": "echo '\nError: The root \"npm test\" command is intentionally disabled.\n\nInstead, run specific test suites:\n - npm run test:trace:core\n - npm run test:appsec\n - etc.\n\nOr run individual test files:\n npx mocha path/to/test.spec.js\n\nSee CONTRIBUTING.md (Testing section) for more details.\n' && exit 1",
@@ -60,6 +61,7 @@
60
61
  "test:integration": "mocha --timeout 60000 \"integration-tests/*.spec.js\"",
61
62
  "test:integration:aiguard": "mocha --timeout 60000 \"integration-tests/aiguard/*.spec.js\"",
62
63
  "test:integration:appsec": "mocha --timeout 60000 \"integration-tests/appsec/*.spec.js\"",
64
+ "test:integration:bun": "mocha --timeout 60000 \"integration-tests/bun/*.spec.js\"",
63
65
  "test:integration:cucumber": "mocha --timeout 60000 \"integration-tests/cucumber/*.spec.js\"",
64
66
  "test:integration:cypress": "mocha --timeout 60000 \"integration-tests/cypress/*.spec.js\"",
65
67
  "test:integration:debugger": "mocha --timeout 60000 \"integration-tests/debugger/*.spec.js\"",
@@ -132,14 +134,14 @@
132
134
  ],
133
135
  "dependencies": {
134
136
  "dc-polyfill": "^0.1.10",
135
- "import-in-the-middle": "^2.0.6"
137
+ "import-in-the-middle": "^3.0.0"
136
138
  },
137
139
  "optionalDependencies": {
138
140
  "@datadog/libdatadog": "0.8.1",
139
141
  "@datadog/native-appsec": "11.0.1",
140
142
  "@datadog/native-iast-taint-tracking": "4.1.0",
141
143
  "@datadog/native-metrics": "3.1.1",
142
- "@datadog/openfeature-node-server": "^0.3.3",
144
+ "@datadog/openfeature-node-server": "^1.1.0",
143
145
  "@datadog/pprof": "5.13.4",
144
146
  "@datadog/wasm-js-rewriter": "5.0.1",
145
147
  "@opentelemetry/api": ">=1.0.0 <1.10.0",
@@ -147,13 +149,14 @@
147
149
  "oxc-parser": "^0.115.0"
148
150
  },
149
151
  "devDependencies": {
152
+ "@actions/core": "^3.0.0",
153
+ "@actions/github": "^9.0.0",
150
154
  "@babel/helpers": "^7.28.6",
151
155
  "@eslint/eslintrc": "^3.3.1",
152
156
  "@eslint/js": "^9.39.2",
153
157
  "@msgpack/msgpack": "^3.1.3",
154
158
  "@openfeature/core": "^1.8.1",
155
159
  "@openfeature/server-sdk": "~1.20.0",
156
- "@snyk/github-codeowners": "^1.1.0",
157
160
  "@stylistic/eslint-plugin": "^5.7.1",
158
161
  "@types/mocha": "^10.0.10",
159
162
  "@types/node": "^18.19.106",
@@ -161,15 +164,16 @@
161
164
  "axios": "^1.13.4",
162
165
  "benchmark": "^2.1.4",
163
166
  "body-parser": "^2.2.2",
164
- "bun": "1.3.8",
167
+ "bun": "1.3.10",
168
+ "codeowners-audit": "^2.0.0",
165
169
  "eslint": "^9.39.2",
166
- "eslint-plugin-cypress": "^5.2.1",
170
+ "eslint-plugin-cypress": "^6.1.0",
167
171
  "eslint-plugin-import": "^2.32.0",
168
172
  "eslint-plugin-jsdoc": "^62.5.0",
169
173
  "eslint-plugin-mocha": "^11.2.0",
170
174
  "eslint-plugin-n": "^17.23.2",
171
175
  "eslint-plugin-promise": "^7.2.1",
172
- "eslint-plugin-unicorn": "^62.0.0",
176
+ "eslint-plugin-unicorn": "^63.0.0",
173
177
  "express": "^5.1.0",
174
178
  "glob": "^10.4.5",
175
179
  "globals": "^17.2.0",
@@ -1,95 +1,45 @@
1
1
  'use strict'
2
2
 
3
- /*
4
- This rewriter is basically a JavaScript version of Orchestrion-JS. The goal is
5
- not to replace Orchestrion-JS, but rather to make it easier and faster to write
6
- new integrations in the short-term, especially as many changes to the rewriter
7
- will be needed as all the patterns we need have not been identified yet. This
8
- will avoid the back and forth of having to make Rust changes to an external
9
- library for every integration change or addition that requires something new.
10
-
11
- In the meantime, we'll work concurrently on a change to Orchestrion-JS that
12
- adds an "arbitrary transform" or "plugin" system that can be used from
13
- JavaScript, in order to enable quick iteration while still using Orchestrion-JS.
14
- Once that's done we'll use that, so that we can remove this JS approach and
15
- return to using Orchestrion-JS.
16
-
17
- The long term goal is to backport any additional features we add to the JS
18
- rewriter (or using the plugin system in Orchestrion-JS once we're using that)
19
- to Orchestrion-JS once we're confident that the implementation is fairly
20
- complete and has all features we need.
21
-
22
- Here is a list of the additions and changes in this rewriter compared to
23
- Orchestrion-JS that will need to be backported:
24
-
25
- (NOTE: Please keep this list up-to-date whenever new features are added)
26
-
27
- - Supports an `astQuery` field to filter AST nodes with an esquery query. This
28
- is mostly meant to be used when experimenting or if what needs to be queried
29
- is not a function. We'll see over time if something like this is needed to be
30
- backported or if it can be replaced by simpler queries.
31
- - Supports replacing methods of child class instances in the base constructor.
32
- - Supports tracing iterator (sync/async) returning functions (sync/async).
33
- */
34
-
35
3
  const { readFileSync } = require('fs')
36
4
  const { join } = require('path')
37
- const semifies = require('../../../../../vendor/dist/semifies')
38
5
  const log = require('../../../../dd-trace/src/log')
39
- const { getEnvironmentVariable } = require('../../../../dd-trace/src/config/helper')
40
- const { transform } = require('./transformer')
41
- const { generate, parse, traverse } = require('./compiler')
42
6
  const instrumentations = require('./instrumentations')
7
+ const { create } = require('./orchestrion')
43
8
 
44
- const NODE_OPTIONS = getEnvironmentVariable('NODE_OPTIONS')
45
-
46
- /** @type {Record<string, Set<string>>} map of module base name to supported function query versions */
47
- const supported = {}
9
+ /** @type {Record<string, string>} map of module base name to version */
10
+ const moduleVersions = {}
48
11
  const disabled = new Set()
49
-
50
- // TODO: Source maps without `--enable-source-maps`.
51
- const enableSourceMaps = NODE_OPTIONS?.includes('--enable-source-maps') ||
52
- process.execArgv?.some(arg => arg.includes('--enable-source-maps'))
53
-
54
- let SourceMapGenerator
12
+ const matcher = create(instrumentations, 'dc-polyfill')
55
13
 
56
14
  function rewrite (content, filename, format) {
57
15
  if (!content) return content
16
+ if (!filename.includes('node_modules')) return content
58
17
 
59
- const sourceType = format === 'module' ? 'module' : 'script'
60
-
61
- try {
62
- let ast
63
-
64
- filename = filename.replace('file://', '')
65
-
66
- for (const inst of instrumentations) {
67
- const { astQuery, functionQuery = {}, module: { name, versionRange, filePath } } = inst
18
+ filename = filename.replace('file://', '')
68
19
 
69
- if (disabled.has(name)) continue
70
- if (!filename.endsWith(`${name}/${filePath}`)) continue
71
- if (!satisfies(filename, filePath, versionRange)) continue
20
+ const moduleType = format === 'module' ? 'esm' : 'cjs'
21
+ const [modulePath] = filename.split('/node_modules/').reverse()
22
+ const moduleParts = modulePath.split('/')
23
+ const splitIndex = moduleParts[0].startsWith('@') ? 2 : 1
24
+ const moduleName = moduleParts.slice(0, splitIndex).join('/')
25
+ const filePath = moduleParts.slice(splitIndex).join('/')
26
+ const version = getVersion(filename, filePath)
72
27
 
73
- ast ??= parse(content.toString(), { range: true, sourceType })
28
+ if (disabled.has(moduleName)) return content
74
29
 
75
- const query = astQuery || fromFunctionQuery(functionQuery)
76
- const state = { ...inst, sourceType, functionQuery }
30
+ const transformer = matcher.getTransformer(moduleName, version, filePath)
77
31
 
78
- traverse(ast, query, (...args) => transform(state, ...args))
79
- }
32
+ if (!transformer) return content
80
33
 
81
- if (ast) {
82
- if (!enableSourceMaps) return generate(ast)
34
+ try {
35
+ // TODO: pass existing sourcemap as input for remapping
36
+ const { code, map } = transformer.transform(content, moduleType)
83
37
 
84
- // TODO: Can we use the same version of `source-map` that DI uses?
85
- SourceMapGenerator ??= require('../../../../../vendor/dist/@datadog/source-map').SourceMapGenerator
38
+ if (!map) return code
86
39
 
87
- const sourceMap = new SourceMapGenerator({ file: filename })
88
- const code = generate(ast, { sourceMap })
89
- const map = Buffer.from(sourceMap.toString()).toString('base64')
40
+ const inlineMap = Buffer.from(map).toString('base64')
90
41
 
91
- return code + '\n' + `//# sourceMappingURL=data:application/json;base64,${map}`
92
- }
42
+ return code + '\n' + `//# sourceMappingURL=data:application/json;base64,${inlineMap}`
93
43
  } catch (e) {
94
44
  log.error(e)
95
45
  }
@@ -101,55 +51,20 @@ function disable (instrumentation) {
101
51
  disabled.add(instrumentation)
102
52
  }
103
53
 
104
- function satisfies (filename, filePath, versions) {
54
+ function getVersion (filename, filePath) {
105
55
  const [basename] = filename.split(filePath)
106
56
 
107
- supported[basename] ??= new Set()
108
-
109
- if (!supported[basename].has(versions)) {
57
+ if (!moduleVersions[basename]) {
110
58
  try {
111
59
  const pkg = JSON.parse(readFileSync(
112
60
  join(basename, 'package.json'), 'utf8'
113
61
  ))
114
62
 
115
- if (semifies(pkg.version, versions)) {
116
- supported[basename].add(versions)
117
- }
63
+ moduleVersions[basename] = pkg.version
118
64
  } catch {}
119
65
  }
120
66
 
121
- return supported[basename].has(versions)
122
- }
123
-
124
- // TODO: Support index
125
- function fromFunctionQuery (functionQuery) {
126
- const { methodName, functionName, expressionName, className } = functionQuery
127
- const queries = []
128
-
129
- if (className) {
130
- queries.push(
131
- `[id.name="${className}"]`,
132
- `[id.name="${className}"] > ClassExpression`,
133
- `[id.name="${className}"] > ClassBody > [key.name="${methodName}"] > [async]`,
134
- `[id.name="${className}"] > ClassExpression > ClassBody > [key.name="${methodName}"] > [async]`
135
- )
136
- } else if (methodName) {
137
- queries.push(
138
- `ClassBody > [key.name="${methodName}"] > [async]`,
139
- `Property[key.name="${methodName}"] > [async]`
140
- )
141
- }
142
-
143
- if (functionName) {
144
- queries.push(`FunctionDeclaration[id.name="${functionName}"][async]`)
145
- } else if (expressionName) {
146
- queries.push(
147
- `FunctionExpression[id.name="${expressionName}"][async]`,
148
- `ArrowFunctionExpression[id.name="${expressionName}"][async]`
149
- )
150
- }
151
-
152
- return queries.join(', ')
67
+ return moduleVersions[basename]
153
68
  }
154
69
 
155
70
  module.exports = { rewrite, disable }
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const log = require('../../../../dd-trace/src/log')
3
+ const log = require('../../../../../dd-trace/src/log')
4
4
 
5
5
  // eslint-disable-next-line camelcase, no-undef
6
6
  const runtimeRequire = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require
@@ -25,7 +25,7 @@ const compiler = {
25
25
  log.error(e)
26
26
 
27
27
  // Fallback for when OXC is not available.
28
- const meriyah = require('../../../../../vendor/dist/meriyah')
28
+ const meriyah = require('../../../../../../vendor/dist/meriyah')
29
29
 
30
30
  compiler.parse = (sourceText, { range, sourceType } = {}) => {
31
31
  return meriyah.parse(sourceText.toString(), {
@@ -40,7 +40,7 @@ const compiler = {
40
40
  },
41
41
 
42
42
  generate: (...args) => {
43
- const astring = require('../../../../../vendor/dist/astring')
43
+ const astring = require('../../../../../../vendor/dist/astring')
44
44
 
45
45
  compiler.generate = astring.generate
46
46
 
@@ -48,7 +48,7 @@ const compiler = {
48
48
  },
49
49
 
50
50
  traverse: (ast, query, visitor) => {
51
- const esquery = require('../../../../../vendor/dist/esquery').default
51
+ const esquery = require('../../../../../../vendor/dist/esquery').default
52
52
 
53
53
  compiler.traverse = (ast, query, visitor) => {
54
54
  return esquery.traverse(ast, esquery.parse(query), visitor)
@@ -58,7 +58,7 @@ const compiler = {
58
58
  },
59
59
 
60
60
  query: (ast, query) => {
61
- const esquery = require('../../../../../vendor/dist/esquery').default
61
+ const esquery = require('../../../../../../vendor/dist/esquery').default
62
62
 
63
63
  compiler.query = esquery.query
64
64
 
@@ -0,0 +1,43 @@
1
+ 'use strict'
2
+
3
+ /*
4
+ This folder is basically a JavaScript version of Orchestrion-JS. The goal is
5
+ not to replace Orchestrion-JS, but rather to make it easier and faster to write
6
+ new integrations in the short-term, especially as many changes to the rewriter
7
+ will be needed as all the patterns we need have not been identified yet. This
8
+ will avoid the back and forth of having to make Rust changes to an external
9
+ library for every integration change or addition that requires something new.
10
+
11
+ In the meantime, we'll work concurrently on a change to Orchestrion-JS that
12
+ adds an "arbitrary transform" or "plugin" system that can be used from
13
+ JavaScript, in order to enable quick iteration while still using Orchestrion-JS.
14
+ Once that's done we'll use that, so that we can remove this JS approach and
15
+ return to using Orchestrion-JS.
16
+
17
+ The long term goal is to backport any additional features we add to the JS
18
+ rewriter (or using the plugin system in Orchestrion-JS once we're using that)
19
+ to Orchestrion-JS once we're confident that the implementation is fairly
20
+ complete and has all features we need.
21
+
22
+ Here is a list of the additions and changes in this rewriter compared to
23
+ Orchestrion-JS that will need to be backported:
24
+
25
+ (NOTE: Please keep this list up-to-date whenever new features are added)
26
+
27
+ - Supports an `astQuery` field to filter AST nodes with an esquery query. This
28
+ is mostly meant to be used when experimenting or if what needs to be queried
29
+ is not a function. We'll see over time if something like this is needed to be
30
+ backported or if it can be replaced by simpler queries.
31
+ - Supports replacing methods of child class instances in the base constructor.
32
+ - Supports tracing iterator (sync/async) returning functions (sync/async).
33
+ */
34
+
35
+ /* eslint-disable camelcase */
36
+
37
+ const { InstrumentationMatcher } = require('./matcher')
38
+
39
+ function create (configs, dc_module) {
40
+ return new InstrumentationMatcher(configs, dc_module)
41
+ }
42
+
43
+ module.exports = { create }
@@ -0,0 +1,49 @@
1
+ 'use strict'
2
+
3
+ /* eslint-disable camelcase */
4
+
5
+ const semifies = require('../../../../../../vendor/dist/semifies')
6
+ const { Transformer } = require('./transformer')
7
+
8
+ // TODO: addTransform
9
+
10
+ class InstrumentationMatcher {
11
+ #configs = []
12
+ #dc_module = null
13
+ #transformers = {}
14
+
15
+ constructor (configs, dc_module) {
16
+ this.#configs = configs
17
+ this.#dc_module = dc_module || 'diagnostics_channel'
18
+ }
19
+
20
+ free () {
21
+ this.#transformers = {}
22
+ }
23
+
24
+ getTransformer (module_name, version, file_path) {
25
+ const id = `${module_name}/${file_path}@${version}`
26
+
27
+ if (this.#transformers[id]) return this.#transformers[id]
28
+
29
+ const configs = this.#configs.filter(({ module: { name, filePath, versionRange } }) =>
30
+ name === module_name &&
31
+ filePath === file_path &&
32
+ semifies(version, versionRange)
33
+ )
34
+
35
+ if (configs.length === 0) return
36
+
37
+ this.#transformers[id] = new Transformer(
38
+ module_name,
39
+ version,
40
+ file_path,
41
+ configs,
42
+ this.#dc_module
43
+ )
44
+
45
+ return this.#transformers[id]
46
+ }
47
+ }
48
+
49
+ module.exports = { InstrumentationMatcher }
@@ -0,0 +1,121 @@
1
+ 'use strict'
2
+
3
+ /* eslint-disable camelcase */
4
+
5
+ const { generate, parse, traverse } = require('./compiler')
6
+ const transforms = require('./transforms')
7
+
8
+ let SourceMapConsumer
9
+ let SourceMapGenerator
10
+
11
+ class Transformer {
12
+ #module_name = null
13
+ #file_path = null
14
+ #configs = []
15
+ #dc_module = null
16
+
17
+ // TODO: module_name false for user module
18
+ constructor (module_name, _version, file_path, configs, dc_module) {
19
+ this.#module_name = module_name
20
+ this.#file_path = file_path
21
+ this.#configs = configs
22
+ this.#dc_module = dc_module
23
+ }
24
+
25
+ free () {
26
+ // Freeing is not needed for a JavaScript implementation.
27
+ }
28
+
29
+ transform (code, module_type, sourcemap) {
30
+ if (!code) return { code }
31
+
32
+ const sourceType = module_type === 'esm' ? 'module' : 'script'
33
+
34
+ let ast
35
+
36
+ for (const config of this.#configs) {
37
+ const { astQuery, functionQuery = {} } = config
38
+
39
+ ast ??= parse(code.toString(), { range: true, sourceType })
40
+
41
+ const query = astQuery || this.#fromFunctionQuery(functionQuery)
42
+ const state = { ...config, dcModule: this.#dc_module, sourceType, functionQuery }
43
+
44
+ state.operator = this.#getOperator(state)
45
+
46
+ traverse(ast, query, (...args) => this.#visit(state, ...args))
47
+ }
48
+
49
+ if (ast) {
50
+ SourceMapConsumer ??= require('../../../../../../vendor/dist/@datadog/source-map').SourceMapConsumer
51
+ SourceMapGenerator ??= require('../../../../../../vendor/dist/@datadog/source-map').SourceMapGenerator
52
+
53
+ const file = `${this.#module_name}/${this.#file_path}`
54
+ const sourceMapInput = sourcemap ? new SourceMapConsumer(sourcemap) : { file }
55
+ const sourceMap = new SourceMapGenerator(sourceMapInput)
56
+ const code = generate(ast, { sourceMap })
57
+ const map = sourceMap.toString()
58
+
59
+ return { code, map }
60
+ }
61
+
62
+ return { code }
63
+ }
64
+
65
+ #visit (state, ...args) {
66
+ const transform = transforms[state.operator]
67
+ const { index } = state.functionQuery
68
+
69
+ if (index !== undefined) {
70
+ state.functionIndex = ++state.functionIndex || 0
71
+
72
+ if (index !== state.functionIndex) return
73
+ }
74
+
75
+ transform(state, ...args)
76
+ }
77
+
78
+ #getOperator ({ functionQuery: { kind } }) {
79
+ switch (kind) {
80
+ case 'Async': return 'tracePromise'
81
+ case 'AsyncIterator': return 'traceAsyncIterator'
82
+ case 'Callback': return 'traceCallback'
83
+ case 'Iterator': return 'traceIterator'
84
+ case 'Sync': return 'traceSync'
85
+ }
86
+ }
87
+
88
+ #fromFunctionQuery (functionQuery) {
89
+ const { functionName, expressionName, className } = functionQuery
90
+ const method = functionQuery.methodName || functionQuery.privateMethodName
91
+ const type = functionQuery.privateMethodName ? 'PrivateIdentifier' : 'Identifier'
92
+ const queries = []
93
+
94
+ if (className) {
95
+ queries.push(
96
+ `[id.name="${className}"]`,
97
+ `[id.name="${className}"] > ClassExpression`,
98
+ `[id.name="${className}"] > ClassBody > [key.name="${method}"][key.type=${type}] > [async]`,
99
+ `[id.name="${className}"] > ClassExpression > ClassBody > [key.name="${method}"][key.type=${type}] > [async]`
100
+ )
101
+ } else if (method) {
102
+ queries.push(
103
+ `ClassBody > [key.name="${method}"][key.type=${type}] > [async]`,
104
+ `Property[key.name="${method}"][key.type=${type}] > [async]`
105
+ )
106
+ }
107
+
108
+ if (functionName) {
109
+ queries.push(`FunctionDeclaration[id.name="${functionName}"][async]`)
110
+ } else if (expressionName) {
111
+ queries.push(
112
+ `FunctionExpression[id.name="${expressionName}"][async]`,
113
+ `ArrowFunctionExpression[id.name="${expressionName}"][async]`
114
+ )
115
+ }
116
+
117
+ return queries.join(', ')
118
+ }
119
+ }
120
+
121
+ module.exports = { Transformer }
@@ -8,13 +8,13 @@ const tracingChannelPredicate = (node) => (
8
8
  )
9
9
 
10
10
  const transforms = module.exports = {
11
- tracingChannelImport ({ sourceType }, node) {
11
+ tracingChannelImport ({ dcModule, sourceType }, node) {
12
12
  if (node.body.some(tracingChannelPredicate)) return
13
13
 
14
14
  const index = node.body.findIndex(child => child.directive === 'use strict')
15
15
  const code = sourceType === 'module'
16
- ? 'import { tracingChannel as tr_ch_apm_tracingChannel } from "diagnostics_channel"'
17
- : 'const {tracingChannel: tr_ch_apm_tracingChannel} = require("diagnostics_channel")'
16
+ ? `import { tracingChannel as tr_ch_apm_tracingChannel } from "${dcModule}"`
17
+ : `const {tracingChannel: tr_ch_apm_tracingChannel} = require("${dcModule}")`
18
18
 
19
19
  node.body.splice(index + 1, 0, parse(code, { sourceType }).body[0])
20
20
  },
@@ -196,11 +196,11 @@ function wrapSuper (_state, node) {
196
196
  }
197
197
 
198
198
  function wrapCallback (state, node) {
199
- const { channelName, functionQuery: { index = -1 } } = state
199
+ const { channelName, functionQuery: { callbackIndex = -1 } } = state
200
200
  const channelVariable = 'tr_ch_apm$' + channelName.replaceAll(':', '_')
201
201
  const wrapper = parse(`
202
202
  function wrapper () {
203
- const __apm$cb = Array.prototype.at.call(arguments, ${index});
203
+ const __apm$cb = Array.prototype.at.call(arguments, ${callbackIndex});
204
204
  const __apm$ctx = {
205
205
  arguments,
206
206
  self: this,
@@ -235,7 +235,7 @@ function wrapCallback (state, node) {
235
235
  if (typeof __apm$cb !== 'function') {
236
236
  return __apm$traced();
237
237
  }
238
- Array.prototype.splice.call(arguments, ${index}, 1, __apm$wrappedCb);
238
+ Array.prototype.splice.call(arguments, ${callbackIndex}, 1, __apm$wrappedCb);
239
239
 
240
240
  return ${channelVariable}.start.runStores(__apm$ctx, () => {
241
241
  try {