dd-trace 6.13.0 → 6.15.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 (208) hide show
  1. package/LICENSE-3rdparty.csv +3 -1
  2. package/ci/init.js +8 -3
  3. package/ci/test-optimization-validation/ci-package-scripts.js +2 -2
  4. package/ci/test-optimization-validation/ci-remediation.js +6 -6
  5. package/ci/test-optimization-validation/command-blocker.js +7 -5
  6. package/ci/test-optimization-validation/framework-adapters/cucumber.js +1 -1
  7. package/ci/test-optimization-validation/framework-adapters/vitest.js +3 -3
  8. package/ci/test-optimization-validation/package-check.js +1 -1
  9. package/ci/test-optimization-validation/report-writer.js +7 -5
  10. package/ci/vitest-no-worker-init-runner-setup.mjs +8 -0
  11. package/ci/vitest-no-worker-init-setup.mjs +13 -13
  12. package/ci/vitest-no-worker-init-v5-setup.mjs +4 -0
  13. package/index.d.ts +8 -0
  14. package/package.json +13 -10
  15. package/packages/datadog-esbuild/index.js +3 -1
  16. package/packages/datadog-instrumentations/src/cypress-config.js +19 -17
  17. package/packages/datadog-instrumentations/src/fs.js +24 -20
  18. package/packages/datadog-instrumentations/src/google-cloud-vertexai.js +2 -2
  19. package/packages/datadog-instrumentations/src/graphql.js +16 -16
  20. package/packages/datadog-instrumentations/src/helpers/graphql-jit-runtime.js +504 -0
  21. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
  22. package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +265 -0
  23. package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +1 -2
  24. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +95 -30
  25. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +0 -25
  26. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql-jit.js +83 -0
  27. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql.js +85 -1
  28. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
  29. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/mercurius.js +30 -12
  30. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +14 -5
  31. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +251 -0
  32. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +11 -1
  33. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +562 -27
  34. package/packages/datadog-instrumentations/src/jest.js +166 -45
  35. package/packages/datadog-instrumentations/src/knex.js +76 -0
  36. package/packages/datadog-instrumentations/src/mariadb.js +6 -0
  37. package/packages/datadog-instrumentations/src/mocha/common.js +59 -2
  38. package/packages/datadog-instrumentations/src/mocha/main.js +66 -20
  39. package/packages/datadog-instrumentations/src/mocha/utils.js +2 -1
  40. package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +98 -0
  41. package/packages/datadog-instrumentations/src/mocha/worker.js +12 -8
  42. package/packages/datadog-instrumentations/src/net.js +14 -17
  43. package/packages/datadog-instrumentations/src/next.js +212 -62
  44. package/packages/datadog-instrumentations/src/openai.js +42 -40
  45. package/packages/datadog-instrumentations/src/pg.js +15 -0
  46. package/packages/datadog-instrumentations/src/playwright.js +113 -13
  47. package/packages/datadog-instrumentations/src/rum-browser-scripts.js +40 -0
  48. package/packages/datadog-instrumentations/src/sequelize.js +94 -2
  49. package/packages/datadog-instrumentations/src/undici.js +53 -4
  50. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +37 -3
  51. package/packages/datadog-instrumentations/src/vitest-main.js +92 -9
  52. package/packages/datadog-instrumentations/src/vitest-worker.js +71 -14
  53. package/packages/datadog-instrumentations/src/webdriverio.js +965 -6
  54. package/packages/datadog-plugin-amqp10/src/consumer.js +3 -1
  55. package/packages/datadog-plugin-amqp10/src/producer.js +3 -1
  56. package/packages/datadog-plugin-amqp10/src/util.js +2 -1
  57. package/packages/datadog-plugin-avsc/src/schema_iterator.js +6 -1
  58. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +16 -5
  59. package/packages/datadog-plugin-cassandra-driver/src/index.js +8 -3
  60. package/packages/datadog-plugin-cucumber/src/index.js +0 -2
  61. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +262 -48
  62. package/packages/datadog-plugin-google-cloud-pubsub/src/client.js +4 -1
  63. package/packages/datadog-plugin-graphql/src/execute.js +998 -196
  64. package/packages/datadog-plugin-graphql/src/index.js +27 -7
  65. package/packages/datadog-plugin-graphql/src/jit.js +136 -0
  66. package/packages/datadog-plugin-graphql/src/request.js +18 -27
  67. package/packages/datadog-plugin-graphql/src/resolve-error.js +108 -0
  68. package/packages/datadog-plugin-graphql/src/tools/signature.js +1 -2
  69. package/packages/datadog-plugin-graphql/src/tools/transforms.js +45 -3
  70. package/packages/datadog-plugin-graphql/src/utils.js +208 -120
  71. package/packages/datadog-plugin-graphql/src/validate.js +10 -23
  72. package/packages/datadog-plugin-jest/src/index.js +0 -6
  73. package/packages/datadog-plugin-langchain/src/tracing.js +7 -0
  74. package/packages/datadog-plugin-mocha/src/index.js +403 -75
  75. package/packages/datadog-plugin-mongodb-core/src/query.js +6 -0
  76. package/packages/datadog-plugin-mysql/src/index.js +41 -15
  77. package/packages/datadog-plugin-net/src/tcp.js +3 -1
  78. package/packages/datadog-plugin-next/src/index.js +63 -29
  79. package/packages/datadog-plugin-playwright/src/index.js +167 -27
  80. package/packages/datadog-plugin-undici/src/index.js +27 -41
  81. package/packages/datadog-plugin-vitest/src/index.js +6 -12
  82. package/packages/dd-trace/src/aiguard/evaluation.js +2 -1
  83. package/packages/dd-trace/src/aiguard/messages/anthropic.js +8 -1
  84. package/packages/dd-trace/src/aiguard/messages/openai.js +8 -1
  85. package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +20 -6
  86. package/packages/dd-trace/src/appsec/api_security/index.js +23 -6
  87. package/packages/dd-trace/src/appsec/api_security/sampler.js +99 -40
  88. package/packages/dd-trace/src/appsec/graphql.js +6 -2
  89. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +3 -3
  90. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +2 -1
  91. package/packages/dd-trace/src/appsec/iast/telemetry/span-tags.js +8 -2
  92. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/command-sensitive-analyzer.js +3 -1
  93. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +531 -95
  94. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +1 -1
  95. package/packages/dd-trace/src/appsec/lambda.js +44 -26
  96. package/packages/dd-trace/src/ci-visibility/encode/json-encoder.js +32 -2
  97. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +8 -8
  98. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +6 -4
  99. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +20 -4
  100. package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +5 -173
  101. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +7 -6
  102. package/packages/dd-trace/src/ci-visibility/exporters/agents.js +54 -0
  103. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/writer.js +1 -2
  104. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +394 -225
  105. package/packages/dd-trace/src/ci-visibility/exporters/limits.js +9 -0
  106. package/packages/dd-trace/src/ci-visibility/exporters/request.js +323 -31
  107. package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +42 -0
  108. package/packages/dd-trace/src/ci-visibility/final-flush.js +2 -1
  109. package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +57 -18
  110. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +0 -3
  111. package/packages/dd-trace/src/ci-visibility/requests/request.js +11 -0
  112. package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +164 -71
  113. package/packages/dd-trace/src/ci-visibility/requests/video-request.js +180 -0
  114. package/packages/dd-trace/src/ci-visibility/telemetry.js +10 -7
  115. package/packages/dd-trace/src/ci-visibility/test-video.js +61 -0
  116. package/packages/dd-trace/src/config/generated-config-types.d.ts +4 -0
  117. package/packages/dd-trace/src/config/index.js +73 -13
  118. package/packages/dd-trace/src/config/parsers.js +11 -1
  119. package/packages/dd-trace/src/config/stable.js +3 -3
  120. package/packages/dd-trace/src/config/supported-configurations.json +22 -2
  121. package/packages/dd-trace/src/crashtracking/crashtracker.js +82 -18
  122. package/packages/dd-trace/src/debugger/config.js +12 -1
  123. package/packages/dd-trace/src/debugger/constants.js +1 -0
  124. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +2 -1
  125. package/packages/dd-trace/src/debugger/devtools_client/request-options.js +28 -0
  126. package/packages/dd-trace/src/debugger/devtools_client/send.js +7 -40
  127. package/packages/dd-trace/src/debugger/devtools_client/status.js +11 -6
  128. package/packages/dd-trace/src/debugger/devtools_client/tags.js +41 -0
  129. package/packages/dd-trace/src/debugger/index.js +23 -3
  130. package/packages/dd-trace/src/dogstatsd.js +510 -76
  131. package/packages/dd-trace/src/encode/0.4-cross-payload.js +170 -0
  132. package/packages/dd-trace/src/encode/0.4.js +5 -5
  133. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +9 -8
  134. package/packages/dd-trace/src/evp_proxy/direct.js +3 -25
  135. package/packages/dd-trace/src/exporters/agent/index.js +2 -1
  136. package/packages/dd-trace/src/exporters/agent/writer.js +24 -7
  137. package/packages/dd-trace/src/exporters/agentless/index.js +7 -9
  138. package/packages/dd-trace/src/exporters/agentless/writer.js +103 -119
  139. package/packages/dd-trace/src/exporters/common/agents.js +28 -6
  140. package/packages/dd-trace/src/exporters/common/buffering-exporter.js +2 -2
  141. package/packages/dd-trace/src/exporters/common/final-flush-request-tracker.js +191 -0
  142. package/packages/dd-trace/src/exporters/common/proxy.js +52 -0
  143. package/packages/dd-trace/src/exporters/common/request.js +42 -14
  144. package/packages/dd-trace/src/exporters/common/url.js +40 -1
  145. package/packages/dd-trace/src/exporters/common/writer.js +9 -7
  146. package/packages/dd-trace/src/flush.js +6 -3
  147. package/packages/dd-trace/src/id.js +14 -0
  148. package/packages/dd-trace/src/index.js +5 -2
  149. package/packages/dd-trace/src/llmobs/experiments/dataset.js +2 -1
  150. package/packages/dd-trace/src/llmobs/index.js +32 -32
  151. package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +22 -71
  152. package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +23 -13
  153. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +7 -0
  154. package/packages/dd-trace/src/llmobs/util.js +0 -33
  155. package/packages/dd-trace/src/llmobs/writers/base.js +49 -32
  156. package/packages/dd-trace/src/log/index.js +7 -1
  157. package/packages/dd-trace/src/{ci-visibility/log-submission → log-submission}/log-submission-plugin.js +48 -19
  158. package/packages/dd-trace/src/openfeature/writers/base.js +14 -12
  159. package/packages/dd-trace/src/openfeature/writers/util.js +1 -1
  160. package/packages/dd-trace/src/opentelemetry/logs/index.js +5 -24
  161. package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +14 -4
  162. package/packages/dd-trace/src/opentelemetry/metrics/index.js +15 -22
  163. package/packages/dd-trace/src/opentelemetry/metrics/otlp_http_metric_exporter.js +14 -4
  164. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +10 -0
  165. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +5 -0
  166. package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +19 -4
  167. package/packages/dd-trace/src/opentelemetry/resource-attributes.js +55 -0
  168. package/packages/dd-trace/src/opentelemetry/trace/index.js +6 -1
  169. package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +10 -0
  170. package/packages/dd-trace/src/opentracing/propagation/text_map.js +101 -20
  171. package/packages/dd-trace/src/payload-tagging/index.js +2 -1
  172. package/packages/dd-trace/src/plugins/ci_plugin.js +1 -6
  173. package/packages/dd-trace/src/plugins/tracing.js +12 -10
  174. package/packages/dd-trace/src/plugins/util/ci.js +3 -1
  175. package/packages/dd-trace/src/plugins/util/inferred_proxy.js +16 -15
  176. package/packages/dd-trace/src/plugins/util/test.js +83 -20
  177. package/packages/dd-trace/src/plugins/util/url.js +2 -2
  178. package/packages/dd-trace/src/plugins/util/web.js +4 -9
  179. package/packages/dd-trace/src/priority_sampler.js +4 -0
  180. package/packages/dd-trace/src/profiling/config.js +1 -0
  181. package/packages/dd-trace/src/profiling/oom.js +7 -2
  182. package/packages/dd-trace/src/profiling/profiler.js +23 -13
  183. package/packages/dd-trace/src/profiling/profilers/space.js +38 -11
  184. package/packages/dd-trace/src/profiling/webspan-utils.js +7 -4
  185. package/packages/dd-trace/src/proxy.js +75 -5
  186. package/packages/dd-trace/src/remote_config/index.js +319 -101
  187. package/packages/dd-trace/src/remote_config/scheduler.js +37 -12
  188. package/packages/dd-trace/src/runtime_metrics/client.js +37 -8
  189. package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +5 -1
  190. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +5 -1
  191. package/packages/dd-trace/src/sampling_rule.js +39 -10
  192. package/packages/dd-trace/src/serverless/telemetry-delivery-tracker.js +4 -8
  193. package/packages/dd-trace/src/serverless/vercel.js +53 -20
  194. package/packages/dd-trace/src/serverless.js +10 -3
  195. package/packages/dd-trace/src/service-naming/schemas/util.js +1 -1
  196. package/packages/dd-trace/src/span_processor.js +18 -3
  197. package/packages/dd-trace/src/span_stats.js +24 -28
  198. package/packages/dd-trace/src/startup-log.js +3 -2
  199. package/packages/dd-trace/src/telemetry/agentless-url.js +28 -0
  200. package/packages/dd-trace/src/telemetry/send-data.js +23 -20
  201. package/packages/dd-trace/src/telemetry/session-propagation.js +3 -2
  202. package/packages/dd-trace/src/tracer.js +4 -5
  203. package/packages/dd-trace/src/tracer_metadata.js +11 -3
  204. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  205. package/vendor/dist/protobufjs/index.js +1 -1
  206. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  207. package/packages/datadog-instrumentations/src/playwright-browser-scripts.js +0 -27
  208. package/packages/dd-trace/src/encode/agentless-json.js +0 -210
@@ -10,14 +10,71 @@
10
10
 
11
11
  const assert = require('node:assert')
12
12
 
13
- const clone = require('../../../../../vendor/dist/rfdc')({ proto: false, circles: false })
13
+ const createRfdc = require('../../../../../vendor/dist/rfdc')
14
+ const clone = createRfdc({ proto: false, circles: false })
14
15
 
15
16
  const { parse, query } = require('./compiler')
16
17
 
17
18
  const functionTypes = new Set(['ArrowFunctionExpression', 'FunctionDeclaration', 'FunctionExpression'])
18
19
  const identifierPattern = /^[$A-Z_a-z][$\w]*$/
19
20
 
20
- module.exports = { awaitContextCallback, waitForAsyncEnd }
21
+ module.exports = {
22
+ awaitContextCallback,
23
+ awaitContextCallbackAtFunctionStart,
24
+ awaitContextCallbackAtTryStart,
25
+ configureGraphqlJitCompileObject,
26
+ configureGraphqlJitDeferredField,
27
+ configureGraphqlJitExecute,
28
+ configureGraphqlJitRuntime,
29
+ configureGraphqlFastPath,
30
+ configureMercuriusRequest,
31
+ waitForAsyncEnd,
32
+ }
33
+
34
+ /**
35
+ * Awaits an optional context callback at the start of the matched node's enclosing async function.
36
+ *
37
+ * @param {Parameters<typeof awaitContextCallback>[0]} state
38
+ * @param {import('estree').Node} node
39
+ * @param {import('estree').Node} _parent
40
+ * @param {import('estree').Node[]} ancestry
41
+ * @returns {void}
42
+ */
43
+ function awaitContextCallbackAtFunctionStart (state, node, _parent, ancestry) {
44
+ let enclosingFunction = functionTypes.has(node.type)
45
+ ? node
46
+ : ancestry.find(ancestor => functionTypes.has(ancestor.type))
47
+ let callbackAncestry = ancestry
48
+
49
+ if (enclosingFunction === node) {
50
+ callbackAncestry = [node, ...ancestry]
51
+ if (!node.async) {
52
+ // Function queries create a synchronous trace wrapper before custom transforms run.
53
+ const [wrappedFunction] = query(node,
54
+ 'VariableDeclarator[id.name="__apm$traced"] > ArrowFunctionExpression > BlockStatement > ' +
55
+ 'VariableDeclaration > VariableDeclarator[id.name="__apm$wrapped"] > ' +
56
+ ':matches(FunctionDeclaration, FunctionExpression)[async=true]')
57
+ if (wrappedFunction) {
58
+ enclosingFunction = wrappedFunction
59
+ callbackAncestry.unshift(wrappedFunction)
60
+ }
61
+ }
62
+ }
63
+ assert(enclosingFunction?.async && enclosingFunction.body?.type === 'BlockStatement',
64
+ 'awaitContextCallbackAtFunctionStart: expected an enclosing async function with a block body')
65
+
66
+ const generatedCallback = createAwaitedContextCallback(
67
+ state,
68
+ enclosingFunction.body,
69
+ callbackAncestry,
70
+ 'awaitContextCallbackAtFunctionStart'
71
+ )
72
+ if (!generatedCallback) return
73
+
74
+ let insertionIndex = 0
75
+ while (typeof enclosingFunction.body.body[insertionIndex]?.directive === 'string') insertionIndex++
76
+ enclosingFunction.body.body.splice(insertionIndex, 0, ...generatedCallback.callbackStatements)
77
+ }
21
78
 
22
79
  /**
23
80
  * Awaits an optional context callback before continuing through a matched conditional branch.
@@ -28,7 +85,8 @@ module.exports = { awaitContextCallback, waitForAsyncEnd }
28
85
  * @param {{
29
86
  * transformOptions?: {
30
87
  * callbackArgumentNames?: string[],
31
- * callbackName?: string
88
+ * callbackName?: string,
89
+ * callbackThis?: boolean
32
90
  * }
33
91
  * }} state
34
92
  * @param {import('estree').IfStatement} node
@@ -40,11 +98,74 @@ function awaitContextCallback (state, node, _parent, ancestry) {
40
98
  assert(node.type === 'IfStatement' && node.consequent?.type === 'BlockStatement',
41
99
  'awaitContextCallback: expected an if statement with a block body')
42
100
 
43
- const { callbackArgumentNames = [], callbackName } = state.transformOptions ?? {}
101
+ const originalStatements = node.consequent.body
102
+ const generatedCallback = createAwaitedContextCallback(
103
+ state,
104
+ node.consequent,
105
+ ancestry,
106
+ 'awaitContextCallback'
107
+ )
108
+ if (!generatedCallback) return
109
+
110
+ const { callbackStatements, callbackVariable } = generatedCallback
111
+ const callbackBranch = parse(`
112
+ if (typeof ${callbackVariable} === 'function') {
113
+ } else {
114
+ }
115
+ `).body[0]
116
+ callbackBranch.consequent.body.push(clone(node))
117
+ callbackBranch.alternate = {
118
+ type: 'BlockStatement',
119
+ body: originalStatements,
120
+ }
121
+ node.consequent.body = [...callbackStatements, callbackBranch]
122
+ }
123
+
124
+ /**
125
+ * Awaits an optional context callback before entering the matched node's enclosing try block.
126
+ *
127
+ * @param {Parameters<typeof awaitContextCallback>[0]} state
128
+ * @param {import('estree').Node} node
129
+ * @param {import('estree').Node} _parent
130
+ * @param {import('estree').Node[]} ancestry
131
+ * @returns {void}
132
+ */
133
+ function awaitContextCallbackAtTryStart (state, node, _parent, ancestry) {
134
+ let tryStatement = node.type === 'TryStatement' ? node : undefined
135
+ for (const ancestor of ancestry) {
136
+ if (tryStatement || functionTypes.has(ancestor.type)) break
137
+ if (ancestor.type === 'TryStatement') tryStatement = ancestor
138
+ }
139
+ assert(tryStatement?.block?.type === 'BlockStatement',
140
+ 'awaitContextCallbackAtTryStart: expected an enclosing try statement with a block body')
141
+
142
+ const generatedCallback = createAwaitedContextCallback(
143
+ state,
144
+ tryStatement.block,
145
+ ancestry,
146
+ 'awaitContextCallbackAtTryStart'
147
+ )
148
+ if (!generatedCallback) return
149
+
150
+ tryStatement.block.body.unshift(...generatedCallback.callbackStatements)
151
+ }
152
+
153
+ /**
154
+ * @param {Parameters<typeof awaitContextCallback>[0]} state
155
+ * @param {import('estree').BlockStatement} insertionTarget
156
+ * @param {import('estree').Node[]} ancestry
157
+ * @param {string} transformName
158
+ * @returns {{
159
+ * callbackStatements: import('estree').Statement[],
160
+ * callbackVariable: string
161
+ * }|undefined}
162
+ */
163
+ function createAwaitedContextCallback (state, insertionTarget, ancestry, transformName) {
164
+ const { callbackArgumentNames = [], callbackName, callbackThis = false } = state.transformOptions ?? {}
44
165
 
45
- assert(identifierPattern.test(callbackName), 'awaitContextCallback: callbackName must be an identifier')
46
- assert(callbackArgumentNames.every(name => identifierPattern.test(name)),
47
- 'awaitContextCallback: callbackArgumentNames must be identifiers')
166
+ assert(identifierPattern.test(callbackName), `${transformName}: callbackName must be an identifier`)
167
+ assert(Array.isArray(callbackArgumentNames) && callbackArgumentNames.every(name => identifierPattern.test(name)),
168
+ `${transformName}: callbackArgumentNames must be identifiers`)
48
169
 
49
170
  let enclosingFunction
50
171
  let hasTraceWrapper = false
@@ -67,35 +188,449 @@ function awaitContextCallback (state, node, _parent, ancestry) {
67
188
  hasTraceWrapper ||= hasContextBinding && hasTracedBinding
68
189
  }
69
190
 
70
- assert(enclosingFunction?.async, 'awaitContextCallback: expected an enclosing async function')
71
- assert(hasTraceWrapper, 'awaitContextCallback: expected an enclosing trace wrapper')
191
+ assert(enclosingFunction?.async, `${transformName}: expected an enclosing async function`)
192
+ assert(hasTraceWrapper, `${transformName}: expected an enclosing trace wrapper`)
72
193
 
73
194
  const callbackVariable = `__apm$${callbackName}`
74
- if (query(node, `[id.name="${callbackVariable}"]`).length > 0) {
75
- return
76
- }
195
+ if (query(insertionTarget, `[id.name="${callbackVariable}"]`).length > 0) return
77
196
 
78
- const originalStatements = node.consequent.body
197
+ const callbackArguments = callbackArgumentNames.join(', ')
198
+ const callbackInvocation = callbackThis
199
+ ? `${callbackVariable}.call(this${callbackArguments ? `, ${callbackArguments}` : ''})`
200
+ : `${callbackVariable}(${callbackArguments})`
79
201
  const callbackStatements = parse(`
80
202
  async function wrapper () {
81
- const ${callbackVariable} = __apm$ctx.${callbackName};
82
- if (typeof ${callbackVariable} === 'function') {
83
- try {
84
- await ${callbackVariable}(${callbackArgumentNames.join(', ')});
85
- } catch {}
86
- if (true) {}
203
+ let ${callbackVariable};
204
+ try {
205
+ ${callbackVariable} = __apm$ctx.${callbackName};
206
+ if (typeof ${callbackVariable} === 'function') {
207
+ await ${callbackInvocation};
208
+ }
209
+ } catch {}
210
+ }
211
+ `).body[0].body.body
212
+
213
+ return {
214
+ callbackStatements,
215
+ callbackVariable,
216
+ }
217
+ }
218
+
219
+ /**
220
+ * @param {object} _state
221
+ * @param {import('estree').FunctionDeclaration} node
222
+ */
223
+ function configureGraphqlJitCompileObject (_state, node) {
224
+ const resolverConditions = query(node, 'IfStatement[test.name="resolver"]')
225
+ const compileTypeCalls = query(node, 'CallExpression[callee.name="compileType"]')
226
+
227
+ assert.strictEqual(
228
+ resolverConditions.length,
229
+ 1,
230
+ 'configureGraphqlJitCompileObject: resolver condition not found'
231
+ )
232
+ assert.strictEqual(
233
+ compileTypeCalls.length,
234
+ 1,
235
+ 'configureGraphqlJitCompileObject: inline compile call not found'
236
+ )
237
+
238
+ const [resolverCondition] = resolverConditions
239
+ const [compileTypeCall] = compileTypeCalls
240
+ const inlineCompileCall = clone(compileTypeCall)
241
+ const argumentProperties = query(resolverCondition.consequent, 'Property[key.name="args"]')
242
+ assert.strictEqual(
243
+ argumentProperties.length,
244
+ 1,
245
+ 'configureGraphqlJitCompileObject: argument compiler not found'
246
+ )
247
+
248
+ const defaultSetup = parse(`
249
+ const ddTraceDefault = context.ddTraceDefaultResolvers && !resolver && alwaysDefer === false
250
+ const ddTraceArguments = ddTraceDefault ? DD_ARGUMENTS : undefined
251
+ `).body
252
+ replaceIdentifier(defaultSetup[1], 'DD_ARGUMENTS', argumentProperties[0].value)
253
+ const [inlineField] = parse(`
254
+ const ddTraceInline = ddTraceDefault
255
+ ? context.ddTraceRuntime.compileDefaultField(
256
+ context,
257
+ DD_PATH,
258
+ type,
259
+ field,
260
+ DD_FIELD_NODES,
261
+ originPaths,
262
+ ddTraceArguments,
263
+ objectStringify(ddTraceArguments.values),
264
+ DD_COMPILED
265
+ )
266
+ : undefined
267
+ `).body
268
+ replaceIdentifier(inlineField, 'DD_PATH', clone(compileTypeCall.arguments[6]))
269
+ replaceIdentifier(inlineField, 'DD_FIELD_NODES', clone(compileTypeCall.arguments[3]))
270
+ replaceIdentifier(inlineField, 'DD_COMPILED', inlineCompileCall)
271
+
272
+ assert(
273
+ insertBeforeStatement(node.body, resolverCondition, [...defaultSetup, inlineField]),
274
+ 'configureGraphqlJitCompileObject: could not insert default field setup'
275
+ )
276
+ resolverCondition.test = parse('resolver || ddTraceDefault').body[0].expression
277
+
278
+ const includedConditions = query(resolverCondition.consequent, 'IfStatement[test.name="alwaysIncluded"]')
279
+ let defaultBody
280
+ if (includedConditions.length === 1) {
281
+ defaultBody = parse(`
282
+ if (alwaysIncluded) {
283
+ body(ddTraceInline)
87
284
  } else {
285
+ body(\`? \${ddTraceInline} : undefined\`)
88
286
  }
287
+ `).body
288
+ } else {
289
+ assert.strictEqual(
290
+ includedConditions.length,
291
+ 0,
292
+ 'configureGraphqlJitCompileObject: ambiguous included condition'
293
+ )
294
+ defaultBody = parse(`
295
+ body(\`? \${ddTraceInline} : undefined\`)
296
+ `).body
297
+ }
298
+
299
+ const originalResolverBody = resolverCondition.consequent
300
+ const [defaultCondition] = parse(`
301
+ if (ddTraceDefault) {}
302
+ `).body
303
+ defaultCondition.consequent.body.push(...defaultBody)
304
+ defaultCondition.alternate = originalResolverBody
305
+ resolverCondition.consequent = {
306
+ type: 'BlockStatement',
307
+ body: [defaultCondition],
308
+ }
309
+ }
310
+
311
+ /**
312
+ * @param {import('estree').Node} root
313
+ * @param {string} name
314
+ * @param {import('estree').Node} replacement
315
+ */
316
+ function replaceIdentifier (root, name, replacement) {
317
+ for (const key of Object.keys(root)) {
318
+ const value = root[key]
319
+ if (!value || typeof value !== 'object') continue
320
+ if (value.type === 'Identifier' && value.name === name) {
321
+ root[key] = clone(replacement)
322
+ } else {
323
+ replaceIdentifier(value, name, replacement)
89
324
  }
90
- `).body[0].body.body
325
+ }
326
+ }
91
327
 
92
- const callbackBranch = callbackStatements[1]
93
- const recheckedBranch = callbackBranch.consequent.body[1]
94
- recheckedBranch.test = clone(node.test)
95
- recheckedBranch.consequent.body.push(...clone(originalStatements))
96
- recheckedBranch.alternate = clone(node.alternate)
97
- callbackBranch.alternate.body.push(...originalStatements)
98
- node.consequent.body = callbackStatements
328
+ /**
329
+ * @param {import('estree').Node} root
330
+ * @param {import('estree').Node} target
331
+ * @param {import('estree').Node[]} statements
332
+ * @returns {boolean}
333
+ */
334
+ function insertBeforeStatement (root, target, statements) {
335
+ for (const key of Object.keys(root)) {
336
+ const value = root[key]
337
+ if (Array.isArray(value)) {
338
+ const index = value.indexOf(target)
339
+ if (index !== -1) {
340
+ value.splice(index, 0, ...statements)
341
+ return true
342
+ }
343
+ for (const entry of value) {
344
+ if (entry && typeof entry === 'object' && insertBeforeStatement(entry, target, statements)) return true
345
+ }
346
+ } else if (value && typeof value === 'object' && insertBeforeStatement(value, target, statements)) {
347
+ return true
348
+ }
349
+ }
350
+ return false
351
+ }
352
+
353
+ /**
354
+ * @param {import('estree').Node} root
355
+ * @param {string} selector
356
+ * @returns {import('estree').Node}
357
+ */
358
+ function queryOne (root, selector) {
359
+ const matches = query(root, selector)
360
+ assert.strictEqual(matches.length, 1, `expected one match for ${selector}`)
361
+ return matches[0]
362
+ }
363
+
364
+ /**
365
+ * @param {object} _state
366
+ * @param {import('estree').FunctionDeclaration} node
367
+ * @param {import('estree').Node} parent
368
+ * @param {import('estree').Node[]} ancestry
369
+ */
370
+ function configureGraphqlFastPath (_state, node, parent, ancestry) {
371
+ assert.strictEqual(node.type, 'FunctionDeclaration', 'configureGraphqlFastPath: expected a function declaration')
372
+ assert(identifierPattern.test(node.id?.name), 'configureGraphqlFastPath: expected a named function')
373
+
374
+ const insertionRoot = Array.isArray(parent?.body)
375
+ ? parent
376
+ : ancestry.find(ancestor => Array.isArray(ancestor?.body) && ancestor.body.includes(parent))
377
+ assert(insertionRoot, 'configureGraphqlFastPath: expected an enclosing statement list')
378
+ const insertionTarget = insertionRoot === parent ? node : parent
379
+
380
+ const originalName = `__apm$original_${node.id.name}`
381
+ assert.strictEqual(
382
+ query(insertionRoot, `VariableDeclarator[id.name="${originalName}"]`).length,
383
+ 0,
384
+ 'configureGraphqlFastPath: original function binding already exists'
385
+ )
386
+ configureGraphqlTraceFastPath(
387
+ node,
388
+ insertionRoot,
389
+ insertionTarget,
390
+ originalName,
391
+ 'configureGraphqlFastPath'
392
+ )
393
+ }
394
+
395
+ /**
396
+ * @param {import('estree').Function} node
397
+ * @param {import('estree').Node} insertionRoot
398
+ * @param {import('estree').Node} insertionTarget
399
+ * @param {string} originalName
400
+ * @param {string} transformName
401
+ */
402
+ function configureGraphqlTraceFastPath (node, insertionRoot, insertionTarget, originalName, transformName) {
403
+ const tracedDeclaration = queryOne(
404
+ node,
405
+ 'VariableDeclaration:has(VariableDeclarator[id.name="__apm$traced"])'
406
+ )
407
+ const tracedBody = queryOne(
408
+ tracedDeclaration,
409
+ 'VariableDeclarator[id.name="__apm$traced"] > ArrowFunctionExpression > BlockStatement'
410
+ )
411
+ const wrappedDeclaration = queryOne(
412
+ tracedBody,
413
+ 'VariableDeclaration:has(VariableDeclarator[id.name="__apm$wrapped"])'
414
+ )
415
+ const wrapped = queryOne(wrappedDeclaration, 'VariableDeclarator[id.name="__apm$wrapped"]')
416
+ const subscriberGuard = queryOne(
417
+ node,
418
+ 'IfStatement[test.operator="!"][consequent.type="ReturnStatement"]' +
419
+ ':has(CallExpression[callee.name="__apm$traced"])'
420
+ )
421
+ const fastCall = subscriberGuard.consequent.argument
422
+ const tracedCalls = query(node, 'CallExpression[callee.name="__apm$traced"]')
423
+ assert.strictEqual(tracedCalls.length, 2, `${transformName}: expected inactive and active traced calls`)
424
+ const activeCall = tracedCalls.find(call => call !== fastCall)
425
+ assert(activeCall, `${transformName}: active traced call not found`)
426
+ assert(functionTypes.has(wrapped.init?.type), `${transformName}: expected a wrapped function`)
427
+
428
+ wrapped.id.name = originalName
429
+ assert(
430
+ insertBeforeStatement(insertionRoot, insertionTarget, [wrappedDeclaration]),
431
+ `${transformName}: could not hoist original function`
432
+ )
433
+
434
+ fastCall.callee = parse(`${originalName}.apply`).body[0].expression
435
+ fastCall.arguments = parse('call(this, arguments)').body[0].expression.arguments
436
+ subscriberGuard.consequent = { type: 'BlockStatement', body: [subscriberGuard.consequent] }
437
+ activeCall.callee = parse(`${originalName}.apply`).body[0].expression
438
+ activeCall.arguments = parse('call(this, __apm$arguments)').body[0].expression.arguments
439
+
440
+ const statements = node.body.body
441
+ const subscriberGuardIndex = statements.indexOf(subscriberGuard)
442
+ const tracedDeclarationIndex = statements.indexOf(tracedDeclaration)
443
+ assert.notStrictEqual(subscriberGuardIndex, -1, `${transformName}: subscriber guard is not top-level`)
444
+ assert.notStrictEqual(tracedDeclarationIndex, -1, `${transformName}: traced declaration is not top-level`)
445
+ statements.splice(subscriberGuardIndex, 1)
446
+ statements.splice(statements.indexOf(tracedDeclaration), 1)
447
+ statements.unshift(subscriberGuard)
448
+ }
449
+
450
+ /**
451
+ * @param {object} _state
452
+ * @param {import('estree').FunctionExpression} node
453
+ * @param {import('estree').Node} _parent
454
+ * @param {import('estree').Node[]} ancestry
455
+ */
456
+ function configureGraphqlJitExecute (_state, node, _parent, ancestry) {
457
+ const context = queryOne(node, 'VariableDeclarator[id.name="__apm$ctx"] > ObjectExpression')
458
+ const wrapped = queryOne(node, 'VariableDeclarator[id.name="__apm$wrapped"]')
459
+
460
+ const createBoundQuery = ancestry.find(ancestor =>
461
+ ancestor.type === 'FunctionDeclaration' && ancestor.id?.name === 'createBoundQuery'
462
+ )
463
+ assert(createBoundQuery, 'configureGraphqlJitExecute: createBoundQuery not found')
464
+
465
+ const retDeclaration = queryOne(
466
+ createBoundQuery,
467
+ 'VariableDeclaration:has(VariableDeclarator[id.name="ret"])'
468
+ )
469
+ assert.strictEqual(
470
+ query(createBoundQuery, 'VariableDeclarator[id.name="__apm$wrapped"]').length,
471
+ 1,
472
+ 'configureGraphqlJitExecute: ambiguous wrapped query declaration'
473
+ )
474
+ assert.strictEqual(
475
+ query(wrapped.init, 'ThisExpression, Super, MetaProperty, Identifier[name="arguments"]').length,
476
+ 0,
477
+ 'configureGraphqlJitExecute: original query depends on its invocation scope'
478
+ )
479
+
480
+ const bindings = parse(`
481
+ const ddResolvers = compilationContext.resolvers
482
+ const ddSchema = compilationContext.schema
483
+ `).body
484
+ const properties = parse(`({
485
+ ddDocument: document,
486
+ ddOperationName: operationName,
487
+ ddPlan,
488
+ ddResolvers,
489
+ ddSchema
490
+ })`).body[0].expression.properties
491
+
492
+ context.properties.push(...properties)
493
+
494
+ assert(
495
+ insertBeforeStatement(createBoundQuery.body, retDeclaration, bindings),
496
+ 'configureGraphqlJitExecute: could not insert query bindings'
497
+ )
498
+ configureGraphqlTraceFastPath(
499
+ node,
500
+ createBoundQuery.body,
501
+ retDeclaration,
502
+ '__apm$wrapped',
503
+ 'configureGraphqlJitExecute'
504
+ )
505
+ }
506
+
507
+ /**
508
+ * @param {object} _state
509
+ * @param {import('estree').FunctionDeclaration} node
510
+ */
511
+ function configureGraphqlJitDeferredField (_state, node) {
512
+ const declarations = query(node, 'VariableDeclaration:has(VariableDeclarator[id.name="resolverCall"])')
513
+ const resolverCalls = query(node, 'VariableDeclarator[id.name="resolverCall"]')
514
+ const executionErrorDeclarations = query(
515
+ node,
516
+ 'VariableDeclaration:has(VariableDeclarator[id.name="executionError"])'
517
+ )
518
+ const executionErrors = query(node, 'VariableDeclarator[id.name="executionError"]')
519
+ const emptyErrors = query(node, 'VariableDeclarator[id.name="emptyError"]')
520
+ assert.strictEqual(
521
+ declarations.length,
522
+ 1,
523
+ 'configureGraphqlJitDeferredField: resolver call declaration not found'
524
+ )
525
+ assert.strictEqual(
526
+ resolverCalls.length,
527
+ 1,
528
+ 'configureGraphqlJitDeferredField: resolver call not found'
529
+ )
530
+ assert.strictEqual(
531
+ executionErrorDeclarations.length,
532
+ 1,
533
+ 'configureGraphqlJitDeferredField: execution error declaration not found'
534
+ )
535
+ assert.strictEqual(executionErrors.length, 1, 'configureGraphqlJitDeferredField: execution error not found')
536
+ assert.strictEqual(emptyErrors.length, 1, 'configureGraphqlJitDeferredField: empty error not found')
537
+
538
+ const [resolverCall] = resolverCalls
539
+ assertGraphqlJitResolverCall(resolverCall.init)
540
+
541
+ const [descriptor] = parse(`
542
+ const ddTraceDescriptorId = context.ddTraceRuntime?.registerField(context, responsePath, {
543
+ fieldName,
544
+ fieldNodes,
545
+ returnType: fieldType,
546
+ parentType
547
+ })
548
+ `).body
549
+ assert(
550
+ insertBeforeStatement(node.body, executionErrorDeclarations[0], [descriptor]),
551
+ 'configureGraphqlJitDeferredField: could not insert descriptor'
552
+ )
553
+
554
+ executionErrors[0].init.arguments[4] = parse(`
555
+ ddTraceDescriptorId === undefined
556
+ ? 'err'
557
+ : '(__context.ddTrace?.jitRuntime.recordResolverError(__context.ddTrace, ' +
558
+ ddTraceDescriptorId + ', err), err)'
559
+ `).body[0].expression
560
+ emptyErrors[0].init.arguments[3] = parse(`
561
+ ddTraceDescriptorId === undefined
562
+ ? '""'
563
+ : '(__context.ddTrace?.jitRuntime.recordResolverError(__context.ddTrace, ' +
564
+ ddTraceDescriptorId + ', err), "")'
565
+ `).body[0].expression
566
+
567
+ const replacement = parse(`
568
+ context.ddTraceRuntime === undefined
569
+ ? DD_CALL
570
+ : context.ddTraceRuntime.compileResolverCall(context, DD_CALL, resolverName, ddTraceDescriptorId)
571
+ `).body[0].expression
572
+ replaceIdentifier(replacement, 'DD_CALL', resolverCall.init)
573
+ resolverCall.init = replacement
574
+ }
575
+
576
+ /**
577
+ * @param {import('estree').Expression | null} source
578
+ */
579
+ function assertGraphqlJitResolverCall (source) {
580
+ assert.strictEqual(source?.type, 'TemplateLiteral', 'configureGraphqlJitDeferredField: unsupported resolver call')
581
+
582
+ const { expressions, quasis } = source
583
+ assert.ok(expressions.length >= 2, 'configureGraphqlJitDeferredField: resolver call expressions not found')
584
+ assert.ok(quasis.length >= 3, 'configureGraphqlJitDeferredField: resolver call segments not found')
585
+ assert.strictEqual(
586
+ expressions[0].name,
587
+ 'GLOBAL_EXECUTION_CONTEXT',
588
+ 'configureGraphqlJitDeferredField: execution context not found'
589
+ )
590
+ assert.strictEqual(
591
+ quasis[1].value.raw,
592
+ '.resolvers.',
593
+ 'configureGraphqlJitDeferredField: resolver map access not found'
594
+ )
595
+ assert.strictEqual(
596
+ expressions[1].name,
597
+ 'resolverName',
598
+ 'configureGraphqlJitDeferredField: resolver name not found'
599
+ )
600
+ assert.ok(quasis[2].value.raw.startsWith('('), 'configureGraphqlJitDeferredField: resolver call not found')
601
+ assert.ok(quasis.at(-1).value.raw.endsWith(')'), 'configureGraphqlJitDeferredField: resolver call end not found')
602
+ }
603
+
604
+ /**
605
+ * @param {object} _state
606
+ * @param {import('estree').FunctionDeclaration} node
607
+ */
608
+ function configureGraphqlJitRuntime (_state, node) {
609
+ node.body.body.unshift(...parse(`
610
+ const ddTraceRuntime = compilationContext.ddTraceRuntime
611
+ const ddPlan = ddTraceRuntime?.finalizeCompilation(compilationContext)
612
+ `).body)
613
+
614
+ const contexts = query(node, 'VariableDeclarator[id.name="executionContext"] > ObjectExpression')
615
+ assert.strictEqual(
616
+ contexts.length,
617
+ 1,
618
+ 'configureGraphqlJitRuntime: execution context not found'
619
+ )
620
+
621
+ const properties = parse(`({
622
+ ddTrace: ddTraceRuntime?.startExecution(parsedVariables.coerced)
623
+ })`).body[0].expression.properties
624
+ contexts[0].properties.push(...properties)
625
+ }
626
+
627
+ /**
628
+ * @param {object} _state
629
+ * @param {import('estree').ObjectExpression} node
630
+ */
631
+ function configureMercuriusRequest (_state, node) {
632
+ const properties = parse('({ ddCacheLimit: opts.cache })').body[0].expression.properties
633
+ node.properties.push(...properties)
99
634
  }
100
635
 
101
636
  /**