dd-trace 5.87.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 (119) hide show
  1. package/LICENSE-3rdparty.csv +60 -32
  2. package/ext/exporters.d.ts +1 -0
  3. package/ext/exporters.js +1 -0
  4. package/ext/tags.js +2 -0
  5. package/index.d.ts +234 -4
  6. package/package.json +18 -11
  7. package/packages/datadog-instrumentations/src/ai.js +54 -90
  8. package/packages/datadog-instrumentations/src/helpers/hook.js +17 -11
  9. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +27 -110
  10. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +103 -0
  11. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/bullmq.js +108 -0
  12. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +2 -1
  13. package/packages/datadog-instrumentations/src/helpers/rewriter/orchestrion/compiler.js +74 -0
  14. package/packages/datadog-instrumentations/src/helpers/rewriter/orchestrion/index.js +43 -0
  15. package/packages/datadog-instrumentations/src/helpers/rewriter/orchestrion/matcher.js +49 -0
  16. package/packages/datadog-instrumentations/src/helpers/rewriter/orchestrion/transformer.js +121 -0
  17. package/packages/datadog-instrumentations/src/helpers/rewriter/{transforms.js → orchestrion/transforms.js} +143 -17
  18. package/packages/datadog-instrumentations/src/jest.js +176 -54
  19. package/packages/datadog-instrumentations/src/kafkajs.js +20 -17
  20. package/packages/datadog-instrumentations/src/playwright.js +1 -1
  21. package/packages/datadog-plugin-amqplib/src/consumer.js +14 -10
  22. package/packages/datadog-plugin-amqplib/src/producer.js +23 -19
  23. package/packages/datadog-plugin-bullmq/src/consumer.js +33 -11
  24. package/packages/datadog-plugin-bullmq/src/producer.js +60 -31
  25. package/packages/datadog-plugin-cucumber/src/index.js +9 -6
  26. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +62 -5
  27. package/packages/datadog-plugin-cypress/src/source-map-utils.js +297 -0
  28. package/packages/datadog-plugin-cypress/src/support.js +52 -9
  29. package/packages/datadog-plugin-jest/src/index.js +12 -2
  30. package/packages/datadog-plugin-jest/src/util.js +2 -1
  31. package/packages/datadog-plugin-kafkajs/src/consumer.js +22 -12
  32. package/packages/datadog-plugin-kafkajs/src/producer.js +33 -22
  33. package/packages/datadog-plugin-mocha/src/index.js +9 -6
  34. package/packages/datadog-plugin-playwright/src/index.js +10 -6
  35. package/packages/datadog-plugin-vitest/src/index.js +13 -8
  36. package/packages/dd-trace/src/aiguard/sdk.js +5 -1
  37. package/packages/dd-trace/src/appsec/iast/analyzers/cookie-analyzer.js +1 -1
  38. package/packages/dd-trace/src/appsec/iast/analyzers/ssrf-analyzer.js +1 -1
  39. package/packages/dd-trace/src/appsec/iast/analyzers/unvalidated-redirect-analyzer.js +1 -1
  40. package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +4 -5
  41. package/packages/dd-trace/src/appsec/iast/path-line.js +36 -25
  42. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/command-sensitive-analyzer.js +1 -1
  43. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +3 -4
  44. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +3 -2
  45. package/packages/dd-trace/src/azure_metadata.js +0 -2
  46. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +1 -1
  47. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +3 -0
  48. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +1 -1
  49. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +4 -1
  50. package/packages/dd-trace/src/ci-visibility/requests/request.js +236 -0
  51. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +1 -1
  52. package/packages/dd-trace/src/config/defaults.js +148 -197
  53. package/packages/dd-trace/src/config/helper.js +43 -1
  54. package/packages/dd-trace/src/config/index.js +38 -14
  55. package/packages/dd-trace/src/config/supported-configurations.json +4125 -512
  56. package/packages/dd-trace/src/constants.js +0 -2
  57. package/packages/dd-trace/src/crashtracking/crashtracker.js +10 -3
  58. package/packages/dd-trace/src/datastreams/checkpointer.js +13 -0
  59. package/packages/dd-trace/src/datastreams/index.js +3 -0
  60. package/packages/dd-trace/src/datastreams/manager.js +9 -0
  61. package/packages/dd-trace/src/datastreams/pathway.js +22 -3
  62. package/packages/dd-trace/src/datastreams/processor.js +140 -4
  63. package/packages/dd-trace/src/encode/agentless-json.js +155 -0
  64. package/packages/dd-trace/src/exporter.js +2 -0
  65. package/packages/dd-trace/src/exporters/agent/writer.js +21 -8
  66. package/packages/dd-trace/src/exporters/agentless/index.js +89 -0
  67. package/packages/dd-trace/src/exporters/agentless/writer.js +184 -0
  68. package/packages/dd-trace/src/exporters/common/request.js +4 -4
  69. package/packages/dd-trace/src/llmobs/plugins/ai/index.js +5 -3
  70. package/packages/dd-trace/src/opentelemetry/context_manager.js +19 -46
  71. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +3 -4
  72. package/packages/dd-trace/src/opentracing/propagation/text_map.js +3 -5
  73. package/packages/dd-trace/src/opentracing/span.js +6 -4
  74. package/packages/dd-trace/src/pkg.js +1 -1
  75. package/packages/dd-trace/src/plugins/ci_plugin.js +57 -5
  76. package/packages/dd-trace/src/plugins/database.js +15 -2
  77. package/packages/dd-trace/src/plugins/util/test.js +48 -0
  78. package/packages/dd-trace/src/profiling/exporter_cli.js +1 -0
  79. package/packages/dd-trace/src/propagation-hash/index.js +145 -0
  80. package/packages/dd-trace/src/proxy.js +6 -1
  81. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +1 -1
  82. package/packages/dd-trace/src/startup-log.js +53 -19
  83. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  84. package/vendor/dist/@datadog/source-map/index.js +1 -1
  85. package/vendor/dist/@isaacs/ttlcache/index.js +1 -1
  86. package/vendor/dist/@opentelemetry/core/index.js +1 -1
  87. package/vendor/dist/@opentelemetry/resources/index.js +1 -1
  88. package/vendor/dist/astring/index.js +1 -1
  89. package/vendor/dist/crypto-randomuuid/index.js +1 -1
  90. package/vendor/dist/escape-string-regexp/index.js +1 -1
  91. package/vendor/dist/esquery/index.js +1 -1
  92. package/vendor/dist/ignore/index.js +1 -1
  93. package/vendor/dist/istanbul-lib-coverage/index.js +1 -1
  94. package/vendor/dist/jest-docblock/index.js +1 -1
  95. package/vendor/dist/jsonpath-plus/index.js +1 -1
  96. package/vendor/dist/limiter/index.js +1 -1
  97. package/vendor/dist/lodash.sortby/index.js +1 -1
  98. package/vendor/dist/lru-cache/index.js +1 -1
  99. package/vendor/dist/meriyah/index.js +1 -1
  100. package/vendor/dist/module-details-from-path/index.js +1 -1
  101. package/vendor/dist/mutexify/promise/index.js +1 -1
  102. package/vendor/dist/opentracing/index.js +1 -1
  103. package/vendor/dist/path-to-regexp/index.js +1 -1
  104. package/vendor/dist/pprof-format/index.js +1 -1
  105. package/vendor/dist/protobufjs/index.js +1 -1
  106. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  107. package/vendor/dist/retry/index.js +1 -1
  108. package/vendor/dist/rfdc/index.js +1 -1
  109. package/vendor/dist/semifies/index.js +1 -1
  110. package/vendor/dist/shell-quote/index.js +1 -1
  111. package/vendor/dist/source-map/index.js +1 -1
  112. package/vendor/dist/source-map/lib/util/index.js +1 -1
  113. package/vendor/dist/tlhunter-sorted-set/index.js +1 -1
  114. package/vendor/dist/ttl-set/index.js +1 -1
  115. package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +0 -33
  116. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/bullmq.json +0 -106
  117. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secrets-rules.js +0 -741
  118. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-regex.js +0 -11
  119. package/packages/dd-trace/src/scope/noop/scope.js +0 -21
@@ -0,0 +1,108 @@
1
+ 'use strict'
2
+
3
+ module.exports = [
4
+ {
5
+ module: {
6
+ name: 'bullmq',
7
+ versionRange: '>=5.66.0',
8
+ filePath: 'dist/cjs/classes/queue.js',
9
+ },
10
+ functionQuery: {
11
+ methodName: 'add',
12
+ className: 'Queue',
13
+ kind: 'Async',
14
+ },
15
+ channelName: 'Queue_add',
16
+ },
17
+ {
18
+ module: {
19
+ name: 'bullmq',
20
+ versionRange: '>=5.66.0',
21
+ filePath: 'dist/cjs/classes/queue.js',
22
+ },
23
+ functionQuery: {
24
+ methodName: 'addBulk',
25
+ className: 'Queue',
26
+ kind: 'Async',
27
+ },
28
+ channelName: 'Queue_addBulk',
29
+ },
30
+ {
31
+ module: {
32
+ name: 'bullmq',
33
+ versionRange: '>=5.66.0',
34
+ filePath: 'dist/cjs/classes/worker.js',
35
+ },
36
+ functionQuery: {
37
+ methodName: 'callProcessJob',
38
+ className: 'Worker',
39
+ kind: 'Async',
40
+ },
41
+ channelName: 'Worker_callProcessJob',
42
+ },
43
+ {
44
+ module: {
45
+ name: 'bullmq',
46
+ versionRange: '>=5.66.0',
47
+ filePath: 'dist/cjs/classes/flow-producer.js',
48
+ },
49
+ functionQuery: {
50
+ methodName: 'add',
51
+ className: 'FlowProducer',
52
+ kind: 'Async',
53
+ },
54
+ channelName: 'FlowProducer_add',
55
+ },
56
+ {
57
+ module: {
58
+ name: 'bullmq',
59
+ versionRange: '>=5.66.0',
60
+ filePath: 'dist/esm/classes/queue.js',
61
+ },
62
+ functionQuery: {
63
+ methodName: 'add',
64
+ className: 'Queue',
65
+ kind: 'Async',
66
+ },
67
+ channelName: 'Queue_add',
68
+ },
69
+ {
70
+ module: {
71
+ name: 'bullmq',
72
+ versionRange: '>=5.66.0',
73
+ filePath: 'dist/esm/classes/queue.js',
74
+ },
75
+ functionQuery: {
76
+ methodName: 'addBulk',
77
+ className: 'Queue',
78
+ kind: 'Async',
79
+ },
80
+ channelName: 'Queue_addBulk',
81
+ },
82
+ {
83
+ module: {
84
+ name: 'bullmq',
85
+ versionRange: '>=5.66.0',
86
+ filePath: 'dist/esm/classes/worker.js',
87
+ },
88
+ functionQuery: {
89
+ methodName: 'callProcessJob',
90
+ className: 'Worker',
91
+ kind: 'Async',
92
+ },
93
+ channelName: 'Worker_callProcessJob',
94
+ },
95
+ {
96
+ module: {
97
+ name: 'bullmq',
98
+ versionRange: '>=5.66.0',
99
+ filePath: 'dist/esm/classes/flow-producer.js',
100
+ },
101
+ functionQuery: {
102
+ methodName: 'add',
103
+ className: 'FlowProducer',
104
+ kind: 'Async',
105
+ },
106
+ channelName: 'FlowProducer_add',
107
+ },
108
+ ]
@@ -1,6 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  module.exports = [
4
- ...require('./langchain'),
4
+ ...require('./ai'),
5
5
  ...require('./bullmq'),
6
+ ...require('./langchain'),
6
7
  ]
@@ -0,0 +1,74 @@
1
+ 'use strict'
2
+
3
+ const log = require('../../../../../dd-trace/src/log')
4
+
5
+ // eslint-disable-next-line camelcase, no-undef
6
+ const runtimeRequire = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require
7
+
8
+ const compiler = {
9
+ parse: (sourceText, options) => {
10
+ try {
11
+ // TODO: Figure out ESBuild `createRequire` issue and remove this hack.
12
+ const oxc = runtimeRequire(['oxc', 'parser'].join('-'))
13
+
14
+ compiler.parse = (sourceText, options) => {
15
+ const { program, errors } = oxc.parseSync('index.js', sourceText, {
16
+ ...options,
17
+ preserveParens: false,
18
+ })
19
+
20
+ if (errors?.length > 0) throw errors[0]
21
+
22
+ return program
23
+ }
24
+ } catch (e) {
25
+ log.error(e)
26
+
27
+ // Fallback for when OXC is not available.
28
+ const meriyah = require('../../../../../../vendor/dist/meriyah')
29
+
30
+ compiler.parse = (sourceText, { range, sourceType } = {}) => {
31
+ return meriyah.parse(sourceText.toString(), {
32
+ loc: range,
33
+ ranges: range,
34
+ module: sourceType === 'module',
35
+ })
36
+ }
37
+ }
38
+
39
+ return compiler.parse(sourceText, options)
40
+ },
41
+
42
+ generate: (...args) => {
43
+ const astring = require('../../../../../../vendor/dist/astring')
44
+
45
+ compiler.generate = astring.generate
46
+
47
+ return compiler.generate(...args)
48
+ },
49
+
50
+ traverse: (ast, query, visitor) => {
51
+ const esquery = require('../../../../../../vendor/dist/esquery').default
52
+
53
+ compiler.traverse = (ast, query, visitor) => {
54
+ return esquery.traverse(ast, esquery.parse(query), visitor)
55
+ }
56
+
57
+ return compiler.traverse(ast, query, visitor)
58
+ },
59
+
60
+ query: (ast, query) => {
61
+ const esquery = require('../../../../../../vendor/dist/esquery').default
62
+
63
+ compiler.query = esquery.query
64
+
65
+ return compiler.query(ast, query)
66
+ },
67
+ }
68
+
69
+ module.exports = {
70
+ parse: (...args) => compiler.parse(...args),
71
+ generate: (...args) => compiler.generate(...args),
72
+ traverse: (...args) => compiler.traverse(...args),
73
+ query: (...args) => compiler.query(...args),
74
+ }
@@ -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 }
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const { parse, query } = require('./compiler')
3
+ const { parse, query, traverse } = require('./compiler')
4
4
 
5
5
  const tracingChannelPredicate = (node) => (
6
6
  node.specifiers?.[0]?.local?.name === 'tr_ch_apm_tracingChannel' ||
@@ -8,15 +8,15 @@ const tracingChannelPredicate = (node) => (
8
8
  )
9
9
 
10
10
  const transforms = module.exports = {
11
- tracingChannelImport ({ format }, 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
- const code = format === 'module'
16
- ? 'import { tracingChannel as tr_ch_apm_tracingChannel } from "diagnostics_channel"'
17
- : 'const {tracingChannel: tr_ch_apm_tracingChannel} = require("diagnostics_channel")'
15
+ const code = sourceType === 'module'
16
+ ? `import { tracingChannel as tr_ch_apm_tracingChannel } from "${dcModule}"`
17
+ : `const {tracingChannel: tr_ch_apm_tracingChannel} = require("${dcModule}")`
18
18
 
19
- node.body.splice(index + 1, 0, parse(code, { module: format === 'module' }).body[0])
19
+ node.body.splice(index + 1, 0, parse(code, { sourceType }).body[0])
20
20
  },
21
21
 
22
22
  tracingChannelDeclaration (state, node) {
@@ -35,7 +35,9 @@ const transforms = module.exports = {
35
35
  node.body.splice(index + 1, 0, parse(code).body[0])
36
36
  },
37
37
 
38
+ traceAsyncIterator: traceAny,
38
39
  traceCallback: traceAny,
40
+ traceIterator: traceAny,
39
41
  tracePromise: traceAny,
40
42
  traceSync: traceAny,
41
43
  }
@@ -51,18 +53,25 @@ function traceAny (state, node, _parent, ancestry) {
51
53
  }
52
54
 
53
55
  function traceFunction (state, node, program) {
54
- const { operator } = state
55
-
56
56
  transforms.tracingChannelDeclaration(state, program)
57
57
 
58
58
  node.body = wrap(state, {
59
- type: 'ArrowFunctionExpression',
59
+ type: 'FunctionExpression',
60
60
  params: node.params,
61
61
  body: node.body,
62
- async: operator === 'tracePromise',
62
+ async: node.async,
63
63
  expression: false,
64
- generator: false,
65
- })
64
+ generator: node.generator,
65
+ }, program)
66
+
67
+ // The original function no longer contains any calls to `await` or `yield` as
68
+ // the function body is copied to the internal wrapped function, so we set
69
+ // these to false to avoid altering the return value of the wrapper. The old
70
+ // values are instead copied to the new AST node above.
71
+ node.generator = false
72
+ node.async = false
73
+
74
+ wrapSuper(state, node)
66
75
  }
67
76
 
68
77
  function traceInstanceMethod (state, node, program) {
@@ -100,15 +109,19 @@ function traceInstanceMethod (state, node, program) {
100
109
  const fn = ctorBody[1].expression.right
101
110
 
102
111
  fn.async = operator === 'tracePromise'
103
- fn.body = wrap(state, { type: 'Identifier', name: `__apm$${methodName}` })
112
+ fn.body = wrap(state, { type: 'Identifier', name: `__apm$${methodName}` }, program)
113
+
114
+ wrapSuper(state, fn)
104
115
 
105
116
  ctor.value.body.body.push(...ctorBody)
106
117
  }
107
118
 
108
- function wrap (state, node) {
119
+ function wrap (state, node, program) {
109
120
  const { channelName, operator } = state
110
121
 
122
+ if (operator === 'traceAsyncIterator') return wrapIterator(state, node, program)
111
123
  if (operator === 'traceCallback') return wrapCallback(state, node)
124
+ if (operator === 'traceIterator') return wrapIterator(state, node, program)
112
125
 
113
126
  const async = operator === 'tracePromise' ? 'async' : ''
114
127
  const channelVariable = 'tr_ch_apm$' + channelName.replaceAll(':', '_')
@@ -133,12 +146,61 @@ function wrap (state, node) {
133
146
  return wrapper
134
147
  }
135
148
 
149
+ function wrapSuper (_state, node) {
150
+ const members = new Set()
151
+
152
+ traverse(
153
+ node.body,
154
+ '[object.type=Super]',
155
+ (node, parent) => {
156
+ const { name } = node.property
157
+
158
+ let child
159
+
160
+ if (parent.callee) {
161
+ // This is needed because for generator functions we have to move the
162
+ // original function to a nested wrapped function, but we can't use an
163
+ // arrow function because arrow function cannot be generator functions,
164
+ // and `super` cannot be called from a nested function, so we have to
165
+ // rewrite any `super` call to not use the keyword.
166
+ const { expression } = parse(`__apm$super['${name}'].call(this)`).body[0]
167
+
168
+ parent.callee = child = expression.callee
169
+ parent.arguments.unshift(...expression.arguments)
170
+ } else {
171
+ parent.expression = child = parse(`__apm$super['${name}']`).body[0]
172
+ }
173
+
174
+ child.computed = parent.callee.computed
175
+ child.optional = parent.callee.optional
176
+
177
+ members.add(name)
178
+ }
179
+ )
180
+
181
+ for (const name of members) {
182
+ const member = parse(`
183
+ class Wrapper {
184
+ wrapper () {
185
+ __apm$super['${name}'] = super['${name}']
186
+ }
187
+ }
188
+ `).body[0].body.body[0].value.body.body[0]
189
+
190
+ node.body.body.unshift(member)
191
+ }
192
+
193
+ if (members.size > 0) {
194
+ node.body.body.unshift(parse('const __apm$super = {}').body[0])
195
+ }
196
+ }
197
+
136
198
  function wrapCallback (state, node) {
137
- const { channelName, functionQuery: { index = -1 } } = state
199
+ const { channelName, functionQuery: { callbackIndex = -1 } } = state
138
200
  const channelVariable = 'tr_ch_apm$' + channelName.replaceAll(':', '_')
139
201
  const wrapper = parse(`
140
202
  function wrapper () {
141
- const __apm$cb = Array.prototype.at.call(arguments, ${index});
203
+ const __apm$cb = Array.prototype.at.call(arguments, ${callbackIndex});
142
204
  const __apm$ctx = {
143
205
  arguments,
144
206
  self: this,
@@ -173,7 +235,7 @@ function wrapCallback (state, node) {
173
235
  if (typeof __apm$cb !== 'function') {
174
236
  return __apm$traced();
175
237
  }
176
- Array.prototype.splice.call(arguments, ${index}, 1, __apm$wrappedCb);
238
+ Array.prototype.splice.call(arguments, ${callbackIndex}, 1, __apm$wrappedCb);
177
239
 
178
240
  return ${channelVariable}.start.runStores(__apm$ctx, () => {
179
241
  try {
@@ -194,3 +256,67 @@ function wrapCallback (state, node) {
194
256
 
195
257
  return wrapper
196
258
  }
259
+
260
+ function wrapIterator (state, node, program) {
261
+ const { channelName, operator } = state
262
+ const baseChannel = channelName.replaceAll(':', '_')
263
+ const channelVariable = 'tr_ch_apm$' + baseChannel
264
+ const nextChannel = baseChannel + '_next'
265
+ const traceMethod = operator === 'traceAsyncIterator' ? 'tracePromise' : 'traceSync'
266
+ const traceNext = `tr_ch_apm$${nextChannel}.${traceMethod}`
267
+
268
+ transforms.tracingChannelDeclaration({ ...state, channelName: nextChannel }, program)
269
+
270
+ const wrapper = parse(`
271
+ function wrapper () {
272
+ const __apm$traced = () => {
273
+ const __apm$wrapped = () => {};
274
+ return __apm$wrapped.apply(this, arguments);
275
+ };
276
+
277
+ if (!${channelVariable}.start.hasSubscribers) return __apm$traced();
278
+
279
+ {
280
+ const wrap = iter => {
281
+ const { next: iterNext, return: iterReturn, throw: iterThrow } = iter;
282
+
283
+ iter.next = (...args) => ${traceNext}(iterNext, ctx, iter, ...args);
284
+ iter.return = (...args) => ${traceNext}(iterReturn, ctx, iter, ...args);
285
+ iter.throw = (...args) => ${traceNext}(iterThrow, ctx, iter, ...args);
286
+
287
+ return iter;
288
+ };
289
+ const ctx = {
290
+ arguments,
291
+ self: this,
292
+ moduleVersion: "1.0.0"
293
+ };
294
+ const iter = ${channelVariable}.traceSync(__apm$traced, ctx);
295
+
296
+ if (typeof iter.then !== 'function') return wrap(iter);
297
+
298
+ return iter.then(result => {
299
+ ctx.result = result;
300
+
301
+ ${channelVariable}.asyncStart.publish(ctx);
302
+ ${channelVariable}.asyncEnd.publish(ctx);
303
+
304
+ return wrap(result);
305
+ }, err => {
306
+ ctx.error = err;
307
+
308
+ ${channelVariable}.error.publish(ctx);
309
+ ${channelVariable}.asyncStart.publish(ctx);
310
+ ${channelVariable}.asyncEnd.publish(ctx);
311
+
312
+ return Promise.reject(err);
313
+ });
314
+ };
315
+ }
316
+ `).body[0].body // Extract only block statement of function body.
317
+
318
+ // Replace the right-hand side assignment of `const __apm$wrapped = () => {}`.
319
+ query(wrapper, '[id.name=__apm$wrapped]')[0].init = node
320
+
321
+ return wrapper
322
+ }