dd-trace 5.104.0 → 5.105.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 (151) hide show
  1. package/LICENSE-3rdparty.csv +90 -102
  2. package/index.d.ts +82 -3
  3. package/package.json +15 -15
  4. package/packages/datadog-core/src/storage.js +1 -1
  5. package/packages/datadog-instrumentations/src/aerospike.js +1 -1
  6. package/packages/datadog-instrumentations/src/ai.js +8 -7
  7. package/packages/datadog-instrumentations/src/aws-sdk.js +13 -0
  8. package/packages/datadog-instrumentations/src/azure-cosmos.js +7 -0
  9. package/packages/datadog-instrumentations/src/azure-functions.js +3 -0
  10. package/packages/datadog-instrumentations/src/cucumber.js +78 -5
  11. package/packages/datadog-instrumentations/src/dns.js +54 -18
  12. package/packages/datadog-instrumentations/src/fastify.js +142 -82
  13. package/packages/datadog-instrumentations/src/graphql.js +188 -62
  14. package/packages/datadog-instrumentations/src/helpers/ai-messages.js +322 -14
  15. package/packages/datadog-instrumentations/src/helpers/hooks.js +4 -0
  16. package/packages/datadog-instrumentations/src/helpers/instrument.js +2 -1
  17. package/packages/datadog-instrumentations/src/helpers/openai-ai-guard.js +269 -0
  18. package/packages/datadog-instrumentations/src/helpers/promise-instrumentor.js +42 -0
  19. package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
  20. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +2 -3
  21. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/azure-cosmos.js +50 -0
  22. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +2 -0
  23. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/langgraph.js +4 -2
  24. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +85 -0
  25. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +37 -236
  26. package/packages/datadog-instrumentations/src/hono.js +54 -3
  27. package/packages/datadog-instrumentations/src/http/server.js +9 -4
  28. package/packages/datadog-instrumentations/src/jest/coverage-backfill.js +163 -0
  29. package/packages/datadog-instrumentations/src/jest.js +360 -150
  30. package/packages/datadog-instrumentations/src/kafkajs.js +120 -16
  31. package/packages/datadog-instrumentations/src/mocha/main.js +128 -17
  32. package/packages/datadog-instrumentations/src/nats.js +182 -0
  33. package/packages/datadog-instrumentations/src/nyc.js +38 -1
  34. package/packages/datadog-instrumentations/src/openai.js +33 -18
  35. package/packages/datadog-instrumentations/src/oracledb.js +6 -1
  36. package/packages/datadog-instrumentations/src/pino.js +17 -5
  37. package/packages/datadog-instrumentations/src/playwright.js +515 -292
  38. package/packages/datadog-instrumentations/src/router.js +76 -32
  39. package/packages/datadog-instrumentations/src/stripe.js +1 -1
  40. package/packages/datadog-plugin-avsc/src/schema_iterator.js +1 -1
  41. package/packages/datadog-plugin-azure-cosmos/src/index.js +144 -0
  42. package/packages/datadog-plugin-azure-event-hubs/src/producer.js +1 -1
  43. package/packages/datadog-plugin-azure-functions/src/index.js +5 -2
  44. package/packages/datadog-plugin-azure-service-bus/src/producer.js +1 -1
  45. package/packages/datadog-plugin-bunyan/src/index.js +28 -0
  46. package/packages/datadog-plugin-cucumber/src/index.js +17 -3
  47. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +199 -28
  48. package/packages/datadog-plugin-cypress/src/support.js +69 -1
  49. package/packages/datadog-plugin-dns/src/lookup.js +8 -6
  50. package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +1 -1
  51. package/packages/datadog-plugin-graphql/src/execute.js +2 -0
  52. package/packages/datadog-plugin-graphql/src/resolve.js +64 -67
  53. package/packages/datadog-plugin-http/src/server.js +40 -15
  54. package/packages/datadog-plugin-jest/src/index.js +11 -3
  55. package/packages/datadog-plugin-jest/src/util.js +15 -8
  56. package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +1 -1
  57. package/packages/datadog-plugin-kafkajs/src/producer.js +3 -0
  58. package/packages/datadog-plugin-langgraph/src/stream.js +1 -1
  59. package/packages/datadog-plugin-mocha/src/index.js +19 -4
  60. package/packages/datadog-plugin-mongodb-core/src/index.js +281 -40
  61. package/packages/datadog-plugin-nats/src/consumer.js +43 -0
  62. package/packages/datadog-plugin-nats/src/index.js +20 -0
  63. package/packages/datadog-plugin-nats/src/producer.js +62 -0
  64. package/packages/datadog-plugin-nats/src/util.js +33 -0
  65. package/packages/datadog-plugin-next/src/index.js +5 -3
  66. package/packages/datadog-plugin-openai/src/tracing.js +15 -2
  67. package/packages/datadog-plugin-oracledb/src/index.js +13 -2
  68. package/packages/datadog-plugin-pino/src/index.js +42 -0
  69. package/packages/datadog-plugin-playwright/src/index.js +4 -4
  70. package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +1 -1
  71. package/packages/datadog-plugin-rhea/src/producer.js +1 -1
  72. package/packages/datadog-plugin-router/src/index.js +33 -44
  73. package/packages/datadog-plugin-selenium/src/index.js +1 -1
  74. package/packages/datadog-plugin-vitest/src/index.js +5 -13
  75. package/packages/datadog-plugin-winston/src/index.js +30 -0
  76. package/packages/datadog-shimmer/src/shimmer.js +33 -40
  77. package/packages/dd-trace/src/aiguard/index.js +1 -1
  78. package/packages/dd-trace/src/aiguard/sdk.js +1 -1
  79. package/packages/dd-trace/src/appsec/api_security_sampler.js +1 -1
  80. package/packages/dd-trace/src/appsec/index.js +1 -1
  81. package/packages/dd-trace/src/appsec/reporter.js +5 -6
  82. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +1 -1
  83. package/packages/dd-trace/src/appsec/sdk/utils.js +1 -1
  84. package/packages/dd-trace/src/appsec/user_tracking.js +5 -4
  85. package/packages/dd-trace/src/baggage.js +7 -1
  86. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +0 -1
  87. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +25 -13
  88. package/packages/dd-trace/src/ci-visibility/test-optimization-cache.js +70 -6
  89. package/packages/dd-trace/src/config/generated-config-types.d.ts +6 -2
  90. package/packages/dd-trace/src/config/supported-configurations.json +27 -8
  91. package/packages/dd-trace/src/datastreams/writer.js +2 -4
  92. package/packages/dd-trace/src/debugger/devtools_client/condition.js +5 -8
  93. package/packages/dd-trace/src/encode/0.4.js +124 -108
  94. package/packages/dd-trace/src/encode/0.5.js +114 -26
  95. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +31 -23
  96. package/packages/dd-trace/src/encode/agentless-json.js +4 -2
  97. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +32 -13
  98. package/packages/dd-trace/src/encode/span-stats.js +16 -16
  99. package/packages/dd-trace/src/encode/tags-processors.js +16 -0
  100. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +1 -1
  101. package/packages/dd-trace/src/llmobs/plugins/genai/index.js +1 -1
  102. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js +1 -1
  103. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +9 -7
  104. package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
  105. package/packages/dd-trace/src/llmobs/plugins/openai/index.js +1 -1
  106. package/packages/dd-trace/src/llmobs/sdk.js +0 -16
  107. package/packages/dd-trace/src/llmobs/span_processor.js +3 -3
  108. package/packages/dd-trace/src/llmobs/tagger.js +9 -1
  109. package/packages/dd-trace/src/llmobs/telemetry.js +1 -1
  110. package/packages/dd-trace/src/llmobs/util.js +66 -3
  111. package/packages/dd-trace/src/log/index.js +1 -1
  112. package/packages/dd-trace/src/msgpack/chunk.js +394 -10
  113. package/packages/dd-trace/src/msgpack/index.js +96 -2
  114. package/packages/dd-trace/src/openfeature/encoding.js +70 -0
  115. package/packages/dd-trace/src/openfeature/flagging_provider.js +20 -0
  116. package/packages/dd-trace/src/openfeature/span-enrichment-hook.js +143 -0
  117. package/packages/dd-trace/src/openfeature/span-enrichment.js +149 -0
  118. package/packages/dd-trace/src/opentelemetry/span-helpers.js +4 -3
  119. package/packages/dd-trace/src/opentelemetry/span.js +1 -1
  120. package/packages/dd-trace/src/opentracing/propagation/log.js +18 -7
  121. package/packages/dd-trace/src/opentracing/propagation/text_map.js +62 -67
  122. package/packages/dd-trace/src/opentracing/span.js +59 -19
  123. package/packages/dd-trace/src/opentracing/span_context.js +49 -0
  124. package/packages/dd-trace/src/plugins/ci_plugin.js +20 -20
  125. package/packages/dd-trace/src/plugins/database.js +7 -6
  126. package/packages/dd-trace/src/plugins/index.js +4 -0
  127. package/packages/dd-trace/src/plugins/log_injection.js +56 -0
  128. package/packages/dd-trace/src/plugins/log_plugin.js +3 -48
  129. package/packages/dd-trace/src/plugins/outbound.js +1 -1
  130. package/packages/dd-trace/src/plugins/plugin.js +15 -17
  131. package/packages/dd-trace/src/plugins/tracing.js +43 -5
  132. package/packages/dd-trace/src/plugins/util/test.js +236 -13
  133. package/packages/dd-trace/src/plugins/util/web.js +79 -65
  134. package/packages/dd-trace/src/priority_sampler.js +2 -2
  135. package/packages/dd-trace/src/profiling/profiler.js +2 -2
  136. package/packages/dd-trace/src/profiling/profilers/wall.js +10 -4
  137. package/packages/dd-trace/src/sampling_rule.js +7 -7
  138. package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +10 -0
  139. package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +8 -0
  140. package/packages/dd-trace/src/service-naming/source-resolver.js +46 -0
  141. package/packages/dd-trace/src/span_format.js +190 -58
  142. package/packages/dd-trace/src/spanleak.js +1 -1
  143. package/packages/dd-trace/src/standalone/index.js +3 -3
  144. package/packages/dd-trace/src/tagger.js +0 -2
  145. package/vendor/dist/@apm-js-collab/code-transformer/index.js +70 -39
  146. package/vendor/dist/@datadog/sketches-js/LICENSE +10 -36
  147. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  148. package/vendor/dist/protobufjs/index.js +1 -1
  149. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  150. package/packages/dd-trace/src/msgpack/encoder.js +0 -308
  151. package/packages/dd-trace/src/plugins/structured_log_plugin.js +0 -9
@@ -1,5 +1,7 @@
1
1
  'use strict'
2
2
 
3
+ const { AsyncLocalStorage } = require('node:async_hooks')
4
+
3
5
  const shimmer = require('../../datadog-shimmer')
4
6
  const {
5
7
  addHook,
@@ -10,7 +12,13 @@ const ddGlobal = globalThis[Symbol.for('dd-trace')]
10
12
 
11
13
  /** cached objects */
12
14
 
15
+ // `contexts` is the fast resolver-side lookup; `executeCtx` is the fallback
16
+ // when `contextValue` is a primitive and cannot key a WeakMap.
13
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()
14
22
  const documentSources = new WeakMap()
15
23
  const patchedResolvers = new WeakSet()
16
24
  const patchedTypes = new WeakSet()
@@ -62,14 +70,17 @@ function getOperation (document, operationName) {
62
70
  function normalizeArgs (args, defaultFieldResolver) {
63
71
  if (args.length !== 1) return normalizePositional(args, defaultFieldResolver)
64
72
 
65
- args[0].contextValue ||= {}
66
- args[0].fieldResolver = wrapResolve(args[0].fieldResolver || defaultFieldResolver)
73
+ const original = args[0]
74
+ const normalized = {
75
+ ...original,
76
+ fieldResolver: wrapResolve(original.fieldResolver || defaultFieldResolver),
77
+ }
67
78
 
68
- return args[0]
79
+ args[0] = normalized
80
+ return normalized
69
81
  }
70
82
 
71
83
  function normalizePositional (args, defaultFieldResolver) {
72
- args[3] = args[3] || {} // contextValue
73
84
  args[6] = wrapResolve(args[6] || defaultFieldResolver) // fieldResolver
74
85
  args.length = Math.max(args.length, 7)
75
86
 
@@ -84,6 +95,12 @@ function normalizePositional (args, defaultFieldResolver) {
84
95
  }
85
96
  }
86
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'
102
+ }
103
+
87
104
  function wrapParse (parse) {
88
105
  return function (source) {
89
106
  if (!parseStartCh.hasSubscribers) {
@@ -155,14 +172,21 @@ function wrapExecute (execute) {
155
172
  return exe.apply(this, arguments)
156
173
  }
157
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
+
158
181
  const args = normalizeArgs(arguments, defaultFieldResolver)
159
182
  const schema = args.schema
160
183
  const document = args.document
161
184
  const source = documentSources.get(document)
162
185
  const contextValue = args.contextValue
186
+ const keyable = isWeakMapKey(contextValue)
163
187
  const operation = getOperation(document, args.operationName)
164
188
 
165
- if (contexts.has(contextValue)) {
189
+ if (keyable && contexts.has(contextValue)) {
166
190
  return exe.apply(this, arguments)
167
191
  }
168
192
 
@@ -171,19 +195,23 @@ function wrapExecute (execute) {
171
195
  args,
172
196
  docSource: source,
173
197
  source,
174
- fields: Object.create(null),
198
+ fields: new Map(),
175
199
  abortController: new AbortController(),
176
200
  }
177
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
+
178
206
  return startExecuteCh.runStores(ctx, () => {
179
207
  if (schema) {
180
208
  wrapFields(schema._queryType)
181
209
  wrapFields(schema._mutationType)
182
210
  }
183
211
 
184
- contexts.set(contextValue, ctx)
212
+ if (keyable) contexts.set(contextValue, ctx)
185
213
 
186
- return callInAsyncScope(exe, this, arguments, ctx.abortController, (err, res) => {
214
+ const finish = (err, res) => {
187
215
  if (finishResolveCh.hasSubscribers) finishResolvers(ctx)
188
216
 
189
217
  const error = err || (res && res.errors && res.errors[0])
@@ -194,8 +222,16 @@ function wrapExecute (execute) {
194
222
  }
195
223
 
196
224
  ctx.res = res
225
+ if (keyable) contexts.delete(contextValue)
226
+ instrumentedArgs.delete(args)
197
227
  finishExecuteCh.publish(ctx)
198
- })
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))
199
235
  })
200
236
  }
201
237
  }
@@ -207,18 +243,40 @@ function wrapResolve (resolve) {
207
243
  function resolveAsync (source, args, contextValue, info) {
208
244
  if (!startResolveCh.hasSubscribers) return resolve.apply(this, arguments)
209
245
 
210
- const ctx = contexts.get(contextValue)
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()
211
249
 
250
+ /* istanbul ignore if: resolver invoked outside execute(), so no per-execute ctx was registered */
212
251
  if (!ctx) return resolve.apply(this, arguments)
213
252
 
214
253
  const field = assertField(ctx, info, args)
215
254
 
216
- return callInAsyncScope(resolve, this, arguments, ctx.abortController, (err) => {
217
- field.ctx.error = err
218
- field.ctx.info = info
219
- field.ctx.field = field
220
- updateFieldCh.publish(field.ctx)
221
- })
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
+ }
222
280
  }
223
281
 
224
282
  patchedResolvers.add(resolveAsync)
@@ -226,72 +284,130 @@ function wrapResolve (resolve) {
226
284
  return resolveAsync
227
285
  }
228
286
 
229
- function callInAsyncScope (fn, thisArg, args, abortController, cb) {
230
- cb = cb || (() => {})
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
+ }
231
297
 
232
- if (abortController?.signal.aborted) {
298
+ function callInAsyncScope (fn, thisArg, args, abortController, cb) {
299
+ if (abortController.signal.aborted) {
233
300
  cb(null, null)
234
301
  throw new AbortError('Aborted')
235
302
  }
236
303
 
237
304
  try {
238
305
  const result = fn.apply(thisArg, args)
239
- if (result && typeof result.then === 'function') {
306
+ if (result !== null && typeof result?.then === 'function') {
240
307
  return result.then(
241
308
  res => {
242
309
  cb(null, res)
243
310
  return res
244
311
  },
245
- err => {
246
- cb(err)
247
- throw err
312
+ /* istanbul ignore next: graphql.execute() rejects only via custom executors (graphql-yoga / graphql-tools) */
313
+ error => {
314
+ cb(error)
315
+ throw error
248
316
  }
249
317
  )
250
318
  }
251
319
  cb(null, result)
252
320
  return result
253
- } catch (err) {
254
- cb(err)
255
- throw err
256
- }
257
- }
258
-
259
- function pathToArray (path) {
260
- let length = 0
261
- for (let curr = path; curr; curr = curr.prev) {
262
- length += 1
263
- }
264
-
265
- const flattened = new Array(length)
266
- let index = length
267
- for (let curr = path; curr; curr = curr.prev) {
268
- flattened[--index] = curr.key
321
+ } catch (error) {
322
+ cb(error)
323
+ throw error
269
324
  }
270
- return flattened
271
325
  }
272
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
+ */
273
343
  function assertField (rootCtx, info, args) {
274
- const pathInfo = info && info.path
275
-
276
- const path = pathToArray(pathInfo)
277
-
278
- const pathString = path.join('.')
279
- const fields = rootCtx.fields
280
-
281
- let field = fields[pathString]
282
-
283
- if (!field) {
284
- const fieldCtx = { info, rootCtx, args, path, pathString }
285
- startResolveCh.publish(fieldCtx)
286
- field = fields[pathString] = {
287
- error: null,
288
- ctx: fieldCtx,
289
- }
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
290
380
  }
291
381
 
382
+ const field = { error: null, ctx: fieldCtx }
383
+ rootCtx.fields.set(path, field)
384
+ if (collapsedFields !== undefined) collapsedFields.set(pathString, field)
292
385
  return field
293
386
  }
294
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
+
295
411
  function wrapFields (type) {
296
412
  if (!type || !type._fields || patchedTypes.has(type)) {
297
413
  return
@@ -323,14 +439,19 @@ function wrapFieldType (field) {
323
439
  }
324
440
 
325
441
  function finishResolvers ({ fields }) {
326
- for (const field of Object.values(fields)) {
327
- field.ctx.finishTime = field.finishTime
328
- field.ctx.field = field
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
329
450
  if (field.error) {
330
- field.ctx.error = field.error
331
- resolveErrorCh.publish(field.ctx)
451
+ fieldCtx.error = field.error
452
+ resolveErrorCh.publish(fieldCtx)
332
453
  }
333
- finishResolveCh.publish(field.ctx)
454
+ finishResolveCh.publish(fieldCtx)
334
455
  }
335
456
  }
336
457
 
@@ -343,6 +464,11 @@ addHook({ name: '@graphql-tools/executor', versions: ['>=0.0.14'] }, executor =>
343
464
  return executor
344
465
  })
345
466
 
467
+ // TODO(BridgeAR): graphql >=17.0.0-alpha.9 routes execute() through
468
+ // experimentalExecuteIncrementally(), bypassing this hook. The same
469
+ // function returns { initialResult, subsequentResults } for @defer /
470
+ // @stream which callInAsyncScope does not handle — execute finishes
471
+ // before the streamed payloads land.
346
472
  addHook({ name: 'graphql', file: 'execution/execute.js', versions: ['>=0.10'] }, execute => {
347
473
  shimmer.wrap(execute, 'execute', wrapExecute(execute))
348
474
  return execute