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.
- package/ext/tags.js +2 -0
- package/index.d.ts +9 -0
- package/package.json +12 -8
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +26 -111
- package/packages/datadog-instrumentations/src/helpers/rewriter/{compiler.js → orchestrion/compiler.js} +5 -5
- package/packages/datadog-instrumentations/src/helpers/rewriter/orchestrion/index.js +43 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/orchestrion/matcher.js +49 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/orchestrion/transformer.js +121 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/{transforms.js → orchestrion/transforms.js} +6 -6
- package/packages/datadog-instrumentations/src/jest.js +101 -43
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +36 -5
- package/packages/datadog-plugin-cypress/src/source-map-utils.js +297 -0
- package/packages/datadog-plugin-cypress/src/support.js +4 -1
- package/packages/dd-trace/src/aiguard/sdk.js +5 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +1 -0
- package/packages/dd-trace/src/config/index.js +2 -0
- package/packages/dd-trace/src/config/supported-configurations.json +10 -0
- package/packages/dd-trace/src/datastreams/checkpointer.js +13 -0
- package/packages/dd-trace/src/datastreams/index.js +3 -0
- package/packages/dd-trace/src/datastreams/manager.js +9 -0
- package/packages/dd-trace/src/datastreams/processor.js +126 -3
- package/packages/dd-trace/src/encode/agentless-json.js +16 -2
- package/packages/dd-trace/src/exporters/agent/writer.js +7 -8
- package/packages/dd-trace/src/pkg.js +1 -1
- package/packages/dd-trace/src/proxy.js +2 -1
- package/packages/dd-trace/src/startup-log.js +52 -18
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/@datadog/source-map/index.js +1 -1
- package/vendor/dist/@isaacs/ttlcache/index.js +1 -1
- package/vendor/dist/@opentelemetry/core/index.js +1 -1
- package/vendor/dist/@opentelemetry/resources/index.js +1 -1
- package/vendor/dist/astring/index.js +1 -1
- package/vendor/dist/crypto-randomuuid/index.js +1 -1
- package/vendor/dist/escape-string-regexp/index.js +1 -1
- package/vendor/dist/esquery/index.js +1 -1
- package/vendor/dist/ignore/index.js +1 -1
- package/vendor/dist/istanbul-lib-coverage/index.js +1 -1
- package/vendor/dist/jest-docblock/index.js +1 -1
- package/vendor/dist/jsonpath-plus/index.js +1 -1
- package/vendor/dist/limiter/index.js +1 -1
- package/vendor/dist/lodash.sortby/index.js +1 -1
- package/vendor/dist/lru-cache/index.js +1 -1
- package/vendor/dist/meriyah/index.js +1 -1
- package/vendor/dist/module-details-from-path/index.js +1 -1
- package/vendor/dist/mutexify/promise/index.js +1 -1
- package/vendor/dist/opentracing/index.js +1 -1
- package/vendor/dist/path-to-regexp/index.js +1 -1
- package/vendor/dist/pprof-format/index.js +1 -1
- package/vendor/dist/protobufjs/index.js +1 -1
- package/vendor/dist/protobufjs/minimal/index.js +1 -1
- package/vendor/dist/retry/index.js +1 -1
- package/vendor/dist/rfdc/index.js +1 -1
- package/vendor/dist/semifies/index.js +1 -1
- package/vendor/dist/shell-quote/index.js +1 -1
- package/vendor/dist/source-map/index.js +1 -1
- package/vendor/dist/source-map/lib/util/index.js +1 -1
- package/vendor/dist/tlhunter-sorted-set/index.js +1 -1
- package/vendor/dist/ttl-set/index.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/transformer.js +0 -21
package/ext/tags.js
CHANGED
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.
|
|
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": "
|
|
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": "^
|
|
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": "^
|
|
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.
|
|
167
|
+
"bun": "1.3.10",
|
|
168
|
+
"codeowners-audit": "^2.0.0",
|
|
165
169
|
"eslint": "^9.39.2",
|
|
166
|
-
"eslint-plugin-cypress": "^
|
|
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": "^
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
|
|
28
|
+
if (disabled.has(moduleName)) return content
|
|
74
29
|
|
|
75
|
-
|
|
76
|
-
const state = { ...inst, sourceType, functionQuery }
|
|
30
|
+
const transformer = matcher.getTransformer(moduleName, version, filePath)
|
|
77
31
|
|
|
78
|
-
|
|
79
|
-
}
|
|
32
|
+
if (!transformer) return content
|
|
80
33
|
|
|
81
|
-
|
|
82
|
-
|
|
34
|
+
try {
|
|
35
|
+
// TODO: pass existing sourcemap as input for remapping
|
|
36
|
+
const { code, map } = transformer.transform(content, moduleType)
|
|
83
37
|
|
|
84
|
-
|
|
85
|
-
SourceMapGenerator ??= require('../../../../../vendor/dist/@datadog/source-map').SourceMapGenerator
|
|
38
|
+
if (!map) return code
|
|
86
39
|
|
|
87
|
-
|
|
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
|
-
|
|
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
|
|
54
|
+
function getVersion (filename, filePath) {
|
|
105
55
|
const [basename] = filename.split(filePath)
|
|
106
56
|
|
|
107
|
-
|
|
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
|
-
|
|
116
|
-
supported[basename].add(versions)
|
|
117
|
-
}
|
|
63
|
+
moduleVersions[basename] = pkg.version
|
|
118
64
|
} catch {}
|
|
119
65
|
}
|
|
120
66
|
|
|
121
|
-
return
|
|
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('
|
|
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('
|
|
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('
|
|
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('
|
|
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('
|
|
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
|
-
?
|
|
17
|
-
:
|
|
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: {
|
|
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, ${
|
|
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, ${
|
|
238
|
+
Array.prototype.splice.call(arguments, ${callbackIndex}, 1, __apm$wrappedCb);
|
|
239
239
|
|
|
240
240
|
return ${channelVariable}.start.runStores(__apm$ctx, () => {
|
|
241
241
|
try {
|