dd-trace 5.110.0 → 5.111.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/README.md +13 -8
- package/ci/init.js +21 -2
- package/ci/vitest-no-worker-init-setup.mjs +430 -0
- package/ext/tags.js +2 -0
- package/index.d.ts +13 -0
- package/initialize.mjs +5 -6
- package/loader-hook.mjs +76 -55
- package/package.json +9 -9
- package/packages/datadog-instrumentations/src/fs.js +8 -6
- package/packages/datadog-instrumentations/src/graphql.js +26 -495
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql.js +201 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
- package/packages/datadog-instrumentations/src/mongoose.js +14 -2
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +1293 -0
- package/packages/datadog-instrumentations/src/vitest-main.js +1567 -0
- package/packages/datadog-instrumentations/src/vitest-util.js +249 -0
- package/packages/datadog-instrumentations/src/vitest-worker.js +753 -0
- package/packages/datadog-instrumentations/src/vitest.js +11 -1855
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/client.js +2 -2
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/context.js +18 -10
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +47 -11
- package/packages/datadog-plugin-cucumber/src/index.js +1 -1
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +10 -4
- package/packages/datadog-plugin-cypress/src/support.js +45 -1
- package/packages/datadog-plugin-express/src/code_origin.js +1 -1
- package/packages/datadog-plugin-fastify/src/code_origin.js +1 -1
- package/packages/datadog-plugin-graphql/src/execute.js +627 -11
- package/packages/datadog-plugin-graphql/src/index.js +20 -8
- package/packages/datadog-plugin-graphql/src/parse.js +24 -4
- package/packages/datadog-plugin-graphql/src/validate.js +16 -5
- package/packages/datadog-plugin-http/src/index.js +6 -8
- package/packages/datadog-plugin-jest/src/index.js +1 -1
- package/packages/datadog-plugin-mocha/src/index.js +1 -1
- package/packages/datadog-plugin-openai/src/services.js +2 -2
- package/packages/datadog-plugin-playwright/src/index.js +1 -1
- package/packages/datadog-plugin-vitest/src/index.js +69 -63
- package/packages/datadog-shimmer/src/shimmer.js +37 -16
- package/packages/dd-trace/src/aiguard/sdk.js +2 -2
- package/packages/dd-trace/src/appsec/api_security/sampler.js +3 -3
- package/packages/dd-trace/src/appsec/channels.js +3 -1
- package/packages/dd-trace/src/appsec/downstream_requests.js +4 -4
- package/packages/dd-trace/src/appsec/graphql.js +5 -5
- package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +26 -19
- package/packages/dd-trace/src/appsec/iast/security-controls/index.js +2 -2
- package/packages/dd-trace/src/appsec/iast/telemetry/index.js +2 -1
- package/packages/dd-trace/src/appsec/index.js +10 -1
- package/packages/dd-trace/src/appsec/lambda.js +135 -0
- package/packages/dd-trace/src/appsec/reporter.js +49 -10
- package/packages/dd-trace/src/appsec/telemetry/index.js +1 -1
- package/packages/dd-trace/src/appsec/waf/index.js +16 -4
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +4 -4
- package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +21 -6
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +2 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +3 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +55 -13
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +2 -2
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +17 -0
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +47 -26
- package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +2 -2
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +64 -53
- package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +3 -3
- package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +23 -5
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +128 -0
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +287 -0
- package/packages/dd-trace/src/config/defaults.js +3 -2
- package/packages/dd-trace/src/config/generated-config-types.d.ts +61 -57
- package/packages/dd-trace/src/config/helper.js +1 -0
- package/packages/dd-trace/src/config/index.js +35 -22
- package/packages/dd-trace/src/config/major-overrides.js +4 -2
- package/packages/dd-trace/src/config/remote_config.js +1 -1
- package/packages/dd-trace/src/config/supported-configurations.json +90 -56
- package/packages/dd-trace/src/constants.js +7 -0
- package/packages/dd-trace/src/datastreams/processor.js +11 -3
- package/packages/dd-trace/src/exporters/agent/index.js +1 -1
- package/packages/dd-trace/src/exporters/agentless/writer.js +4 -4
- package/packages/dd-trace/src/llmobs/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/base.js +4 -4
- package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
- package/packages/dd-trace/src/llmobs/sdk.js +3 -3
- package/packages/dd-trace/src/llmobs/span_processor.js +1 -1
- package/packages/dd-trace/src/llmobs/tagger.js +3 -3
- package/packages/dd-trace/src/llmobs/writers/base.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +54 -5
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +40 -0
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +169 -0
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +38 -39
- package/packages/dd-trace/src/opentelemetry/trace/index.js +4 -0
- package/packages/dd-trace/src/opentelemetry/tracer_provider.js +1 -1
- package/packages/dd-trace/src/opentracing/tracer.js +6 -1
- package/packages/dd-trace/src/plugin_manager.js +7 -7
- package/packages/dd-trace/src/plugins/ci_plugin.js +12 -3
- package/packages/dd-trace/src/plugins/util/llm.js +6 -1
- package/packages/dd-trace/src/plugins/util/test.js +11 -9
- package/packages/dd-trace/src/profiling/exporter_cli.js +2 -2
- package/packages/dd-trace/src/profiling/exporters/event_serializer.js +3 -3
- package/packages/dd-trace/src/profiling/profilers/wall.js +1 -1
- package/packages/dd-trace/src/proxy.js +8 -8
- package/packages/dd-trace/src/span_processor.js +5 -6
- package/packages/dd-trace/src/span_stats.js +96 -78
- package/packages/dd-trace/src/startup-log.js +2 -1
- package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
- package/packages/dd-trace/src/telemetry/endpoints.js +6 -4
- package/packages/dd-trace/src/telemetry/index.js +2 -2
- package/packages/dd-trace/src/telemetry/logs/index.js +1 -1
- package/packages/dd-trace/src/telemetry/send-data.js +8 -8
- package/packages/dd-trace/src/telemetry/session-propagation.js +1 -1
- package/packages/dd-trace/src/telemetry/telemetry.js +9 -9
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/protobufjs/index.js +1 -1
- package/vendor/dist/protobufjs/minimal/index.js +1 -1
- package/vendor/dist/shell-quote/index.js +1 -1
- package/packages/datadog-plugin-graphql/src/resolve.js +0 -170
|
@@ -1,515 +1,46 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const { addHook, getHooks } = require('./helpers/instrument')
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
// Orchestrion rewriter handles wrapping of:
|
|
6
|
+
// - graphql: execute, parse, validate (CJS + ESM)
|
|
7
|
+
// - @graphql-tools/executor: execute, normalizedExecutor (CJS + ESM)
|
|
8
|
+
// See helpers/rewriter/instrumentations/graphql.js for the full config.
|
|
9
|
+
//
|
|
10
|
+
// The plugin (packages/datadog-plugin-graphql/src/execute.js) handles the
|
|
11
|
+
// `apm:graphql:execute:start` AppSec/WAF contract from inside its bindStart:
|
|
12
|
+
// publishing the channel synchronously runs subscribers, and an
|
|
13
|
+
// `abortController.abort()` from a subscriber is observed by replacing
|
|
14
|
+
// `ctx.arguments[0]` with an object whose getters throw AbortError. The
|
|
15
|
+
// orchestrion-emitted wrapper's `catch { ...; throw err }` block propagates
|
|
16
|
+
// that throw to the caller of graphql.execute. No outer wrap needed.
|
|
10
17
|
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
/** cached objects */
|
|
14
|
-
|
|
15
|
-
// `contexts` is the fast resolver-side lookup; `executeCtx` is the fallback
|
|
16
|
-
// when `contextValue` is a primitive and cannot key a WeakMap.
|
|
17
|
-
const contexts = new WeakMap()
|
|
18
|
-
const executeCtx = new AsyncLocalStorage()
|
|
19
|
-
// Tracks normalized args already instrumented in an outer wrap so graphql-yoga
|
|
20
|
-
// (which stacks `execute` + `normalizedExecutor`) only emits one span per call.
|
|
21
|
-
const instrumentedArgs = new WeakSet()
|
|
22
|
-
const documentSources = new WeakMap()
|
|
23
|
-
const patchedResolvers = new WeakSet()
|
|
24
|
-
const patchedTypes = new WeakSet()
|
|
25
|
-
|
|
26
|
-
/** CHANNELS */
|
|
27
|
-
|
|
28
|
-
// execute channels
|
|
29
|
-
const startExecuteCh = channel('apm:graphql:execute:start')
|
|
30
|
-
const finishExecuteCh = channel('apm:graphql:execute:finish')
|
|
31
|
-
const executeErrorCh = channel('apm:graphql:execute:error')
|
|
32
|
-
|
|
33
|
-
// resolve channels
|
|
34
|
-
const startResolveCh = channel('apm:graphql:resolve:start')
|
|
35
|
-
const finishResolveCh = channel('apm:graphql:resolve:finish')
|
|
36
|
-
const updateFieldCh = channel('apm:graphql:resolve:updateField')
|
|
37
|
-
const resolveErrorCh = channel('apm:graphql:resolve:error')
|
|
38
|
-
|
|
39
|
-
// parse channels
|
|
40
|
-
const parseStartCh = channel('apm:graphql:parser:start')
|
|
41
|
-
const parseFinishCh = channel('apm:graphql:parser:finish')
|
|
42
|
-
const parseErrorCh = channel('apm:graphql:parser:error')
|
|
43
|
-
|
|
44
|
-
// validate channels
|
|
45
|
-
const validateStartCh = channel('apm:graphql:validate:start')
|
|
46
|
-
const validateFinishCh = channel('apm:graphql:validate:finish')
|
|
47
|
-
const validateErrorCh = channel('apm:graphql:validate:error')
|
|
48
|
-
|
|
49
|
-
class AbortError extends Error {
|
|
50
|
-
constructor (message) {
|
|
51
|
-
super(message)
|
|
52
|
-
this.name = 'AbortError'
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const types = new Set(['query', 'mutation', 'subscription'])
|
|
57
|
-
|
|
58
|
-
function getOperation (document, operationName) {
|
|
59
|
-
if (!document || !Array.isArray(document.definitions)) {
|
|
60
|
-
return
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
for (const definition of document.definitions) {
|
|
64
|
-
if (definition && types.has(definition.operation) && (!operationName || definition.name?.value === operationName)) {
|
|
65
|
-
return definition
|
|
66
|
-
}
|
|
67
|
-
}
|
|
18
|
+
for (const hook of getHooks('graphql')) {
|
|
19
|
+
addHook(hook, exports => exports)
|
|
68
20
|
}
|
|
69
21
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const original = args[0]
|
|
74
|
-
const normalized = {
|
|
75
|
-
...original,
|
|
76
|
-
fieldResolver: wrapResolve(original.fieldResolver || defaultFieldResolver),
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
args[0] = normalized
|
|
80
|
-
return normalized
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function normalizePositional (args, defaultFieldResolver) {
|
|
84
|
-
args[6] = wrapResolve(args[6] || defaultFieldResolver) // fieldResolver
|
|
85
|
-
args.length = Math.max(args.length, 7)
|
|
86
|
-
|
|
87
|
-
return {
|
|
88
|
-
schema: args[0],
|
|
89
|
-
document: args[1],
|
|
90
|
-
rootValue: args[2],
|
|
91
|
-
contextValue: args[3],
|
|
92
|
-
variableValues: args[4],
|
|
93
|
-
operationName: args[5],
|
|
94
|
-
fieldResolver: args[6],
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// `WeakMap.set` throws `TypeError` on a non-object key; `get`/`has`/`delete`
|
|
99
|
-
// silently miss. Skip the WeakMap entirely for non-keyable `contextValue`.
|
|
100
|
-
function isWeakMapKey (value) {
|
|
101
|
-
return value !== null && typeof value === 'object'
|
|
22
|
+
for (const hook of getHooks('@graphql-tools/executor')) {
|
|
23
|
+
addHook(hook, exports => exports)
|
|
102
24
|
}
|
|
103
25
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
return parse.apply(this, arguments)
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const ctx = { source }
|
|
111
|
-
return parseStartCh.runStores(ctx, () => {
|
|
112
|
-
try {
|
|
113
|
-
ctx.document = parse.apply(this, arguments)
|
|
114
|
-
const operation = getOperation(ctx.document)
|
|
115
|
-
|
|
116
|
-
if (!operation) return ctx.document
|
|
117
|
-
|
|
118
|
-
if (source) {
|
|
119
|
-
documentSources.set(ctx.document, source.body || source)
|
|
120
|
-
}
|
|
121
|
-
ctx.docSource = documentSources.get(ctx.document)
|
|
122
|
-
|
|
123
|
-
return ctx.document
|
|
124
|
-
} catch (err) {
|
|
125
|
-
void err.stack
|
|
126
|
-
ctx.error = err
|
|
127
|
-
parseErrorCh.publish(ctx)
|
|
128
|
-
|
|
129
|
-
throw err
|
|
130
|
-
} finally {
|
|
131
|
-
parseFinishCh.publish(ctx)
|
|
132
|
-
}
|
|
133
|
-
})
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function wrapValidate (validate) {
|
|
138
|
-
return function (_schema, document, _rules, _typeInfo) {
|
|
139
|
-
if (!validateStartCh.hasSubscribers) {
|
|
140
|
-
return validate.apply(this, arguments)
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const ctx = { docSource: documentSources.get(document), document }
|
|
144
|
-
return validateStartCh.runStores(ctx, () => {
|
|
145
|
-
let errors
|
|
146
|
-
try {
|
|
147
|
-
errors = validate.apply(this, arguments)
|
|
148
|
-
if (errors && errors[0]) {
|
|
149
|
-
ctx.error = errors[0]
|
|
150
|
-
validateErrorCh.publish(ctx)
|
|
151
|
-
}
|
|
152
|
-
return errors
|
|
153
|
-
} catch (err) {
|
|
154
|
-
void err.stack
|
|
155
|
-
ctx.error = err
|
|
156
|
-
validateErrorCh.publish(ctx)
|
|
157
|
-
|
|
158
|
-
throw err
|
|
159
|
-
} finally {
|
|
160
|
-
ctx.errors = errors
|
|
161
|
-
validateFinishCh.publish(ctx)
|
|
162
|
-
}
|
|
163
|
-
})
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
function wrapExecute (execute) {
|
|
168
|
-
return function (exe) {
|
|
169
|
-
const defaultFieldResolver = execute.defaultFieldResolver
|
|
170
|
-
return function () {
|
|
171
|
-
if (!startExecuteCh.hasSubscribers) {
|
|
172
|
-
return exe.apply(this, arguments)
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// The outer wrap leaves its normalized args object in `arguments[0]`; on
|
|
176
|
-
// graphql-yoga's inner wrap that reference is already known here.
|
|
177
|
-
if (instrumentedArgs.has(arguments[0])) {
|
|
178
|
-
return exe.apply(this, arguments)
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const args = normalizeArgs(arguments, defaultFieldResolver)
|
|
182
|
-
const schema = args.schema
|
|
183
|
-
const document = args.document
|
|
184
|
-
const source = documentSources.get(document)
|
|
185
|
-
const contextValue = args.contextValue
|
|
186
|
-
const keyable = isWeakMapKey(contextValue)
|
|
187
|
-
const operation = getOperation(document, args.operationName)
|
|
188
|
-
|
|
189
|
-
if (keyable && contexts.has(contextValue)) {
|
|
190
|
-
return exe.apply(this, arguments)
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
const ctx = {
|
|
194
|
-
operation,
|
|
195
|
-
args,
|
|
196
|
-
docSource: source,
|
|
197
|
-
source,
|
|
198
|
-
fields: new Map(),
|
|
199
|
-
abortController: new AbortController(),
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// Only the object form leaves a stable single-object handle in
|
|
203
|
-
// `arguments[0]` for the inner wrap to see.
|
|
204
|
-
if (args === arguments[0]) instrumentedArgs.add(args)
|
|
205
|
-
|
|
206
|
-
return startExecuteCh.runStores(ctx, () => {
|
|
207
|
-
if (schema) {
|
|
208
|
-
wrapFields(schema._queryType)
|
|
209
|
-
wrapFields(schema._mutationType)
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
if (keyable) contexts.set(contextValue, ctx)
|
|
213
|
-
|
|
214
|
-
const finish = (err, res) => {
|
|
215
|
-
if (finishResolveCh.hasSubscribers) finishResolvers(ctx)
|
|
216
|
-
|
|
217
|
-
const error = err || (res && res.errors && res.errors[0])
|
|
218
|
-
|
|
219
|
-
if (error) {
|
|
220
|
-
ctx.error = error
|
|
221
|
-
executeErrorCh.publish(ctx)
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
ctx.res = res
|
|
225
|
-
if (keyable) contexts.delete(contextValue)
|
|
226
|
-
instrumentedArgs.delete(args)
|
|
227
|
-
finishExecuteCh.publish(ctx)
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// Skip the ALS entry on the common object-`contextValue` path; the
|
|
231
|
-
// resolver reaches `ctx` via the WeakMap there.
|
|
232
|
-
return keyable
|
|
233
|
-
? callInAsyncScope(exe, this, arguments, ctx.abortController, finish)
|
|
234
|
-
: executeCtx.run(ctx, () => callInAsyncScope(exe, this, arguments, ctx.abortController, finish))
|
|
235
|
-
})
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
function wrapResolve (resolve) {
|
|
241
|
-
if (typeof resolve !== 'function' || patchedResolvers.has(resolve)) return resolve
|
|
242
|
-
|
|
243
|
-
function resolveAsync (source, args, contextValue, info) {
|
|
244
|
-
if (!startResolveCh.hasSubscribers) return resolve.apply(this, arguments)
|
|
245
|
-
|
|
246
|
-
// `WeakMap.get(primitive)` returns `undefined`, so the fallback covers
|
|
247
|
-
// executes that ran with a primitive `contextValue`.
|
|
248
|
-
const ctx = contexts.get(contextValue) ?? executeCtx.getStore()
|
|
249
|
-
|
|
250
|
-
/* istanbul ignore if: resolver invoked outside execute(), so no per-execute ctx was registered */
|
|
251
|
-
if (!ctx) return resolve.apply(this, arguments)
|
|
252
|
-
|
|
253
|
-
const field = assertField(ctx, info, args)
|
|
254
|
-
|
|
255
|
-
if (ctx.abortController.signal.aborted) {
|
|
256
|
-
publishResolverFinish(field, null)
|
|
257
|
-
throw new AbortError('Aborted')
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
try {
|
|
261
|
-
const result = resolve.call(this, source, args, contextValue, info)
|
|
262
|
-
if (result !== null && typeof result?.then === 'function') {
|
|
263
|
-
return result.then(
|
|
264
|
-
res => {
|
|
265
|
-
publishResolverFinish(field, null)
|
|
266
|
-
return res
|
|
267
|
-
},
|
|
268
|
-
error => {
|
|
269
|
-
publishResolverFinish(field, error)
|
|
270
|
-
throw error
|
|
271
|
-
}
|
|
272
|
-
)
|
|
273
|
-
}
|
|
274
|
-
publishResolverFinish(field, null)
|
|
275
|
-
return result
|
|
276
|
-
} catch (error) {
|
|
277
|
-
publishResolverFinish(field, error)
|
|
278
|
-
throw error
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
patchedResolvers.add(resolveAsync)
|
|
283
|
-
|
|
284
|
-
return resolveAsync
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
/**
|
|
288
|
-
* @param {{ ctx: object, error: unknown }} field
|
|
289
|
-
* @param {unknown} error
|
|
290
|
-
*/
|
|
291
|
-
function publishResolverFinish (field, error) {
|
|
292
|
-
const fieldCtx = field.ctx
|
|
293
|
-
fieldCtx.error = error
|
|
294
|
-
fieldCtx.field = field
|
|
295
|
-
updateFieldCh.publish(fieldCtx)
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
function callInAsyncScope (fn, thisArg, args, abortController, cb) {
|
|
299
|
-
if (abortController.signal.aborted) {
|
|
300
|
-
cb(null, null)
|
|
301
|
-
throw new AbortError('Aborted')
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
try {
|
|
305
|
-
const result = fn.apply(thisArg, args)
|
|
306
|
-
if (result !== null && typeof result?.then === 'function') {
|
|
307
|
-
return result.then(
|
|
308
|
-
res => {
|
|
309
|
-
cb(null, res)
|
|
310
|
-
return res
|
|
311
|
-
},
|
|
312
|
-
/* istanbul ignore next: graphql.execute() rejects only via custom executors (graphql-yoga / graphql-tools) */
|
|
313
|
-
error => {
|
|
314
|
-
cb(error)
|
|
315
|
-
throw error
|
|
316
|
-
}
|
|
317
|
-
)
|
|
318
|
-
}
|
|
319
|
-
cb(null, result)
|
|
320
|
-
return result
|
|
321
|
-
} catch (error) {
|
|
322
|
-
cb(error)
|
|
323
|
-
throw error
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
/**
|
|
328
|
-
* @typedef {{ prev: PathNode | undefined, key: string | number }} PathNode
|
|
329
|
-
*
|
|
330
|
-
* @typedef {{ error: unknown, ctx: object }} TrackedField
|
|
331
|
-
*/
|
|
332
|
-
|
|
333
|
-
/**
|
|
334
|
-
* @param {{
|
|
335
|
-
* fields: Map<object, TrackedField>,
|
|
336
|
-
* collapse: boolean,
|
|
337
|
-
* collapsedFields?: Map<string, TrackedField>,
|
|
338
|
-
* pathCache?: Map<PathNode, string>,
|
|
339
|
-
* }} rootCtx
|
|
340
|
-
* @param {import('graphql').GraphQLResolveInfo} info
|
|
341
|
-
* @param {Record<string, unknown>} args
|
|
342
|
-
*/
|
|
343
|
-
function assertField (rootCtx, info, args) {
|
|
344
|
-
const path = info.path
|
|
345
|
-
const collapse = rootCtx.collapse
|
|
346
|
-
|
|
347
|
-
const cache = rootCtx.pathCache ??= new Map()
|
|
348
|
-
const prev = path.prev
|
|
349
|
-
const key = path.key
|
|
350
|
-
const segment = collapse && typeof key !== 'string' ? '*' : key
|
|
351
|
-
|
|
352
|
-
const pathString = prev === undefined
|
|
353
|
-
? String(segment)
|
|
354
|
-
: (cache.get(prev) ?? buildCachedPathString(prev, cache, collapse)) + '.' + segment
|
|
355
|
-
cache.set(path, pathString)
|
|
356
|
-
|
|
357
|
-
const fieldCtx = {
|
|
358
|
-
rootCtx,
|
|
359
|
-
args,
|
|
360
|
-
path,
|
|
361
|
-
pathString,
|
|
362
|
-
fieldName: info.fieldName,
|
|
363
|
-
returnType: info.returnType,
|
|
364
|
-
fieldNode: info.fieldNodes[0],
|
|
365
|
-
variableValues: info.variableValues,
|
|
366
|
-
}
|
|
367
|
-
// Publish per resolver call, before the collapse / depth dedupe below.
|
|
368
|
-
// IAST mutates each call's own args object; if siblings 2..N skip the
|
|
369
|
-
// publish, those args objects never get tainted.
|
|
370
|
-
startResolveCh.publish(fieldCtx)
|
|
371
|
-
|
|
372
|
-
let collapsedFields
|
|
373
|
-
if (collapse) {
|
|
374
|
-
collapsedFields = rootCtx.collapsedFields ??= new Map()
|
|
375
|
-
const existing = collapsedFields.get(pathString)
|
|
376
|
-
// Subsequent siblings of a collapsed list share the first sibling's field
|
|
377
|
-
// so updateFieldCh fires for every call and the span's finishTime tracks
|
|
378
|
-
// the last sibling's completion, not the first.
|
|
379
|
-
if (existing !== undefined) return existing
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
const field = { error: null, ctx: fieldCtx }
|
|
383
|
-
rootCtx.fields.set(path, field)
|
|
384
|
-
if (collapsedFields !== undefined) collapsedFields.set(pathString, field)
|
|
385
|
-
return field
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
/**
|
|
389
|
-
* Cold path for assertField. graphql-js inserts a synthetic array-index
|
|
390
|
-
* node between a list field and its items, and that node never reaches a
|
|
391
|
-
* resolver — so assertField has no chance to cache it. The first child of
|
|
392
|
-
* the list item that hits the path cache lands here to walk and populate
|
|
393
|
-
* back to a cached ancestor.
|
|
394
|
-
*
|
|
395
|
-
* @param {PathNode} path
|
|
396
|
-
* @param {Map<PathNode, string>} cache
|
|
397
|
-
* @param {boolean} collapse
|
|
398
|
-
*/
|
|
399
|
-
function buildCachedPathString (path, cache, collapse) {
|
|
400
|
-
const key = path.key
|
|
401
|
-
const segment = collapse && typeof key !== 'string' ? '*' : key
|
|
402
|
-
const prev = path.prev
|
|
403
|
-
|
|
404
|
-
const pathString = prev === undefined
|
|
405
|
-
? String(segment)
|
|
406
|
-
: (cache.get(prev) ?? buildCachedPathString(prev, cache, collapse)) + '.' + segment
|
|
407
|
-
cache.set(path, pathString)
|
|
408
|
-
return pathString
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
function wrapFields (type) {
|
|
412
|
-
if (!type || !type._fields || patchedTypes.has(type)) {
|
|
413
|
-
return
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
patchedTypes.add(type)
|
|
417
|
-
|
|
418
|
-
for (const field of Object.values(type._fields)) {
|
|
419
|
-
wrapFieldResolve(field)
|
|
420
|
-
wrapFieldType(field)
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
function wrapFieldResolve (field) {
|
|
425
|
-
if (!field || !field.resolve) return
|
|
426
|
-
field.resolve = wrapResolve(field.resolve)
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
function wrapFieldType (field) {
|
|
430
|
-
if (!field || !field.type) return
|
|
431
|
-
|
|
432
|
-
let unwrappedType = field.type
|
|
433
|
-
|
|
434
|
-
while (unwrappedType.ofType) {
|
|
435
|
-
unwrappedType = unwrappedType.ofType
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
wrapFields(unwrappedType)
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
function finishResolvers ({ fields }) {
|
|
442
|
-
for (const field of fields.values()) {
|
|
443
|
-
const fieldCtx = field.ctx
|
|
444
|
-
// A depth-gated field publishes startResolveCh for IAST/AppSec but the
|
|
445
|
-
// resolve plugin's start short-circuits before creating a span, so there
|
|
446
|
-
// is no span here to finish.
|
|
447
|
-
if (fieldCtx.currentStore === undefined) continue
|
|
448
|
-
fieldCtx.finishTime = field.finishTime
|
|
449
|
-
fieldCtx.field = field
|
|
450
|
-
if (field.error) {
|
|
451
|
-
fieldCtx.error = field.error
|
|
452
|
-
resolveErrorCh.publish(fieldCtx)
|
|
453
|
-
}
|
|
454
|
-
finishResolveCh.publish(fieldCtx)
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
// The CJS package root re-exports `execute` and `normalizedExecutor` as
|
|
459
|
-
// non-configurable tslib `__exportStar` getters that shimmer cannot rewrite in
|
|
460
|
-
// place, so the inner execute.js hook below carries CJS instead: graphql-yoga
|
|
461
|
-
// (`normalizedExecutor`) and direct callers both funnel through that `execute`.
|
|
462
|
-
// Under iitm (ESM) the namespace is settable and the inner-file hook never
|
|
463
|
-
// matches the `esm/` path, so the root wrap is the only option there.
|
|
464
|
-
addHook({ name: '@graphql-tools/executor', versions: ['>=0.0.14'] }, (executor, _version, isIitm) => {
|
|
465
|
-
/* istanbul ignore if: covered only by the graphql-yoga ESM integration test (subprocess) */
|
|
466
|
-
if (isIitm) {
|
|
467
|
-
shimmer.wrap(executor, 'execute', wrapExecute(executor))
|
|
468
|
-
shimmer.wrap(executor, 'normalizedExecutor', wrapExecute(executor))
|
|
469
|
-
}
|
|
470
|
-
return executor
|
|
471
|
-
})
|
|
472
|
-
|
|
473
|
-
addHook({ name: '@graphql-tools/executor', file: 'cjs/execution/execute.js', versions: ['>=0.0.14'] }, execute => {
|
|
474
|
-
shimmer.wrap(execute, 'execute', wrapExecute(execute))
|
|
475
|
-
return execute
|
|
476
|
-
})
|
|
477
|
-
|
|
478
|
-
// TODO(BridgeAR): graphql >=17.0.0-alpha.9 routes execute() through
|
|
479
|
-
// experimentalExecuteIncrementally(), bypassing this hook. The same
|
|
480
|
-
// function returns { initialResult, subsequentResults } for @defer /
|
|
481
|
-
// @stream which callInAsyncScope does not handle — execute finishes
|
|
482
|
-
// before the streamed payloads land.
|
|
483
|
-
addHook({ name: 'graphql', file: 'execution/execute.js', versions: ['>=0.10'] }, execute => {
|
|
484
|
-
shimmer.wrap(execute, 'execute', wrapExecute(execute))
|
|
485
|
-
return execute
|
|
486
|
-
})
|
|
487
|
-
|
|
488
|
-
addHook({ name: 'graphql', file: 'language/parser.js', versions: ['>=0.10'] }, parser => {
|
|
489
|
-
shimmer.wrap(parser, 'parse', wrapParse)
|
|
490
|
-
return parser
|
|
491
|
-
})
|
|
492
|
-
|
|
493
|
-
addHook({ name: 'graphql', file: 'validation/validate.js', versions: ['>=0.10'] }, validate => {
|
|
494
|
-
shimmer.wrap(validate, 'validate', wrapValidate)
|
|
495
|
-
|
|
496
|
-
return validate
|
|
497
|
-
})
|
|
26
|
+
// Module-load hooks: capture references on ddGlobal for cross-plugin access
|
|
27
|
+
// (read lazily inside each callback so agent.load() between mocha suites can
|
|
28
|
+
// rebind globalThis[dd-trace] without us stashing a stale reference).
|
|
498
29
|
|
|
499
30
|
addHook({ name: 'graphql', file: 'language/printer.js', versions: ['>=0.10'] }, printer => {
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
// happened. Not sure how to handle this so for now use a global.
|
|
503
|
-
ddGlobal.graphql_printer = printer
|
|
31
|
+
const ddGlobal = globalThis[Symbol.for('dd-trace')]
|
|
32
|
+
if (ddGlobal) ddGlobal.graphql_printer = printer
|
|
504
33
|
return printer
|
|
505
34
|
})
|
|
506
35
|
|
|
507
36
|
addHook({ name: 'graphql', file: 'language/visitor.js', versions: ['>=0.10'] }, visitor => {
|
|
508
|
-
ddGlobal
|
|
37
|
+
const ddGlobal = globalThis[Symbol.for('dd-trace')]
|
|
38
|
+
if (ddGlobal) ddGlobal.graphql_visitor = visitor
|
|
509
39
|
return visitor
|
|
510
40
|
})
|
|
511
41
|
|
|
512
42
|
addHook({ name: 'graphql', file: 'utilities/index.js', versions: ['>=0.10'] }, utilities => {
|
|
513
|
-
ddGlobal
|
|
43
|
+
const ddGlobal = globalThis[Symbol.for('dd-trace')]
|
|
44
|
+
if (ddGlobal) ddGlobal.graphql_utilities = utilities
|
|
514
45
|
return utilities
|
|
515
46
|
})
|