dd-trace 5.89.0 → 5.91.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 (71) hide show
  1. package/LICENSE-3rdparty.csv +0 -3
  2. package/index.d.ts +38 -0
  3. package/package.json +12 -11
  4. package/packages/datadog-instrumentations/src/azure-durable-functions.js +75 -0
  5. package/packages/datadog-instrumentations/src/cucumber.js +58 -4
  6. package/packages/datadog-instrumentations/src/elasticsearch.js +12 -3
  7. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
  8. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
  9. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/langgraph.js +30 -0
  10. package/packages/datadog-instrumentations/src/jest.js +31 -2
  11. package/packages/datadog-instrumentations/src/langgraph.js +7 -0
  12. package/packages/datadog-instrumentations/src/mocha/main.js +41 -12
  13. package/packages/datadog-instrumentations/src/mocha/utils.js +6 -1
  14. package/packages/datadog-instrumentations/src/mocha/worker.js +12 -4
  15. package/packages/datadog-instrumentations/src/playwright.js +20 -2
  16. package/packages/datadog-instrumentations/src/prisma.js +4 -2
  17. package/packages/datadog-instrumentations/src/vitest.js +69 -24
  18. package/packages/datadog-plugin-apollo/src/gateway/execute.js +8 -0
  19. package/packages/datadog-plugin-apollo/src/gateway/fetch.js +5 -0
  20. package/packages/datadog-plugin-apollo/src/gateway/plan.js +8 -0
  21. package/packages/datadog-plugin-apollo/src/gateway/postprocessing.js +5 -0
  22. package/packages/datadog-plugin-apollo/src/gateway/request.js +4 -3
  23. package/packages/datadog-plugin-apollo/src/gateway/validate.js +4 -3
  24. package/packages/datadog-plugin-apollo/src/index.js +28 -0
  25. package/packages/datadog-plugin-azure-durable-functions/src/index.js +49 -0
  26. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +12 -2
  27. package/packages/datadog-plugin-cypress/src/support.js +5 -7
  28. package/packages/datadog-plugin-jest/src/index.js +6 -0
  29. package/packages/datadog-plugin-langgraph/src/index.js +24 -0
  30. package/packages/datadog-plugin-langgraph/src/stream.js +41 -0
  31. package/packages/datadog-plugin-playwright/src/index.js +35 -8
  32. package/packages/dd-trace/src/aiguard/noop.js +1 -1
  33. package/packages/dd-trace/src/aiguard/sdk.js +14 -5
  34. package/packages/dd-trace/src/appsec/api_security_sampler.js +22 -1
  35. package/packages/dd-trace/src/appsec/index.js +11 -1
  36. package/packages/dd-trace/src/appsec/reporter.js +28 -11
  37. package/packages/dd-trace/src/appsec/waf/index.js +1 -1
  38. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +4 -4
  39. package/packages/dd-trace/src/config/defaults.js +1 -0
  40. package/packages/dd-trace/src/config/index.js +9 -1
  41. package/packages/dd-trace/src/config/supported-configurations.json +14 -0
  42. package/packages/dd-trace/src/constants.js +2 -0
  43. package/packages/dd-trace/src/crashtracking/crashtracker.js +1 -1
  44. package/packages/dd-trace/src/debugger/devtools_client/config.js +3 -0
  45. package/packages/dd-trace/src/dogstatsd.js +1 -0
  46. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +1 -8
  47. package/packages/dd-trace/src/encode/agentless-json.js +67 -22
  48. package/packages/dd-trace/src/exporters/agentless/index.js +58 -15
  49. package/packages/dd-trace/src/exporters/agentless/writer.js +35 -18
  50. package/packages/dd-trace/src/llmobs/constants/tags.js +2 -0
  51. package/packages/dd-trace/src/llmobs/plugins/anthropic.js +9 -0
  52. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chain.js +11 -0
  53. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +2 -0
  54. package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +114 -0
  55. package/packages/dd-trace/src/llmobs/tagger.js +8 -0
  56. package/packages/dd-trace/src/opentracing/propagation/text_map.js +1 -0
  57. package/packages/dd-trace/src/plugins/apollo.js +7 -2
  58. package/packages/dd-trace/src/plugins/ci_plugin.js +2 -2
  59. package/packages/dd-trace/src/plugins/index.js +2 -0
  60. package/packages/dd-trace/src/plugins/util/ci.js +95 -3
  61. package/packages/dd-trace/src/plugins/util/inferred_proxy.js +36 -2
  62. package/packages/dd-trace/src/plugins/util/test.js +7 -10
  63. package/packages/dd-trace/src/plugins/util/web.js +31 -11
  64. package/packages/dd-trace/src/priority_sampler.js +20 -2
  65. package/packages/dd-trace/src/process-tags/index.js +41 -34
  66. package/packages/dd-trace/src/profiling/profilers/wall.js +9 -1
  67. package/packages/dd-trace/src/proxy.js +4 -0
  68. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +7 -0
  69. package/packages/dd-trace/src/service-naming/schemas/v0/serverless.js +4 -0
  70. package/packages/dd-trace/src/service-naming/schemas/v1/serverless.js +4 -0
  71. package/packages/dd-trace/src/standalone/product.js +2 -1
@@ -61,7 +61,6 @@
61
61
  "crypto-randomuuid","npm:crypto-randomuuid","['MIT']","['Stephen Belanger']"
62
62
  "dc-polyfill","https://github.com/DataDog/dc-polyfill","['MIT']","['Thomas Hunter II']"
63
63
  "dd-trace","https://github.com/DataDog/dd-trace-js","['(Apache-2.0 OR BSD-3-Clause)']","['Datadog Inc. <info@datadoghq.com>']"
64
- "delay","https://github.com/sindresorhus/delay","['MIT']","['Sindre Sorhus']"
65
64
  "detect-newline","https://github.com/sindresorhus/detect-newline","['MIT']","['Sindre Sorhus']"
66
65
  "escape-string-regexp","https://github.com/sindresorhus/escape-string-regexp","['MIT']","['Sindre Sorhus']"
67
66
  "esquery","https://github.com/estools/esquery","['BSD-3-Clause']","['Joel Feenstra']"
@@ -85,7 +84,6 @@
85
84
  "node-gyp-build","https://github.com/prebuild/node-gyp-build","['MIT']","['Mathias Buus']"
86
85
  "opentracing","https://github.com/opentracing/opentracing-javascript","['Apache-2.0']","['opentracing']"
87
86
  "oxc-parser","https://github.com/oxc-project/oxc","['MIT']","['Boshen and oxc contributors']"
88
- "p-limit","https://github.com/sindresorhus/p-limit","['MIT']","['Sindre Sorhus']"
89
87
  "path-to-regexp","https://github.com/pillarjs/path-to-regexp","['MIT']","['pillarjs']"
90
88
  "pprof-format","https://github.com/DataDog/pprof-format","['MIT']","['Datadog Inc.']"
91
89
  "protobufjs","https://github.com/protobufjs/protobuf.js","['BSD-3-Clause']","['Daniel Wirtz']"
@@ -100,6 +98,5 @@
100
98
  "tslib","https://github.com/microsoft/tslib","['0BSD']","['Microsoft Corp.']"
101
99
  "ttl-set","https://github.com/watson/ttl-set","['MIT']","['Thomas Watson']"
102
100
  "undici-types","https://github.com/nodejs/undici","['MIT']","['nodejs']"
103
- "yocto-queue","https://github.com/sindresorhus/yocto-queue","['MIT']","['Sindre Sorhus']"
104
101
  "aws-lambda-nodejs-runtime-interface-client","https://github.com/aws/aws-lambda-nodejs-runtime-interface-client/blob/v2.1.0/src/utils/UserFunction.ts","['Apache-2.0']","['Amazon.com Inc. or its affiliates']"
105
102
  "is-git-url","https://github.com/jonschlinkert/is-git-url/blob/396965ffabf2f46656c8af4c47bef1d69f09292e/index.js#L9C15-L9C87","['MIT']","['Jon Schlinkert']"
package/index.d.ts CHANGED
@@ -228,6 +228,7 @@ interface Plugins {
228
228
  "azure-event-hubs": tracer.plugins.azure_event_hubs;
229
229
  "azure-functions": tracer.plugins.azure_functions;
230
230
  "azure-service-bus": tracer.plugins.azure_service_bus;
231
+ "azure-durable-functions": tracer.plugins.azure_durable_functions
231
232
  "bullmq": tracer.plugins.bullmq;
232
233
  "bunyan": tracer.plugins.bunyan;
233
234
  "cassandra-driver": tracer.plugins.cassandra_driver;
@@ -261,6 +262,7 @@ interface Plugins {
261
262
  "knex": tracer.plugins.knex;
262
263
  "koa": tracer.plugins.koa;
263
264
  "langchain": tracer.plugins.langchain;
265
+ "langgraph": tracer.plugins.langgraph;
264
266
  "mariadb": tracer.plugins.mariadb;
265
267
  "memcached": tracer.plugins.memcached;
266
268
  "microgateway-core": tracer.plugins.microgateway_core;
@@ -1626,6 +1628,10 @@ declare namespace tracer {
1626
1628
  * List of tags associated with the evaluation (e.g. indirect-prompt-injection)
1627
1629
  */
1628
1630
  tags: string[];
1631
+ /**
1632
+ * Sensitive Data Scanner findings from the evaluation.
1633
+ */
1634
+ sds: Object[];
1629
1635
  }
1630
1636
 
1631
1637
  /**
@@ -1641,6 +1647,10 @@ declare namespace tracer {
1641
1647
  * List of tags associated with the evaluation (e.g. indirect-prompt-injection)
1642
1648
  */
1643
1649
  tags: string[];
1650
+ /**
1651
+ * Sensitive Data Scanner findings from the evaluation.
1652
+ */
1653
+ sds: Object[];
1644
1654
  }
1645
1655
 
1646
1656
  /**
@@ -2034,6 +2044,22 @@ declare namespace tracer {
2034
2044
  * @default true
2035
2045
  */
2036
2046
  signature?: boolean;
2047
+
2048
+ /**
2049
+ * An object of optional callbacks to be executed during the respective
2050
+ * phase of an Apollo Gateway operation. Undefined callbacks default to a
2051
+ * noop function.
2052
+ *
2053
+ * @default {}
2054
+ */
2055
+ hooks?: {
2056
+ request?: (span?: Span, ctx?: any) => void;
2057
+ validate?: (span?: Span, ctx?: any) => void;
2058
+ plan?: (span?: Span, ctx?: any) => void;
2059
+ execute?: (span?: Span, ctx?: any) => void;
2060
+ fetch?: (span?: Span, ctx?: any) => void;
2061
+ postprocessing?: (span?: Span, ctx?: any) => void;
2062
+ };
2037
2063
  }
2038
2064
 
2039
2065
  /**
@@ -2097,6 +2123,12 @@ declare namespace tracer {
2097
2123
  */
2098
2124
  interface azure_service_bus extends Integration {}
2099
2125
 
2126
+ /**
2127
+ * This plugin automatically instruments the
2128
+ * durable-functions module
2129
+ */
2130
+ interface azure_durable_functions extends Integration {}
2131
+
2100
2132
  /**
2101
2133
  * This plugin patches the [bunyan](https://github.com/trentm/node-bunyan)
2102
2134
  * to automatically inject trace identifiers in log records when the
@@ -2548,6 +2580,12 @@ declare namespace tracer {
2548
2580
 
2549
2581
  /**
2550
2582
  * This plugin automatically instruments the
2583
+ * [langgraph](https://github.com/npmjs/package/langgraph) library.
2584
+ */
2585
+ interface langgraph extends Instrumentation {}
2586
+
2587
+ /**
2588
+ * This plugin automatically instruments the
2551
2589
  * [ldapjs](https://github.com/ldapjs/node-ldapjs/) module.
2552
2590
  */
2553
2591
  interface ldapjs extends Instrumentation {}
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "dd-trace",
3
- "version": "5.89.0",
3
+ "version": "5.91.0",
4
4
  "description": "Datadog APM tracing client for JavaScript",
5
5
  "main": "index.js",
6
6
  "typings": "index.d.ts",
7
7
  "scripts": {
8
8
  "env": "bash ./plugin-env",
9
+ "prepare": "cd vendor && npm ci --include=dev",
9
10
  "preinstall": "node scripts/preinstall.js",
10
11
  "bench": "node benchmark/index.js",
11
12
  "bench:e2e:test-optimization": "node benchmark/e2e-test-optimization/benchmark-run.js",
@@ -142,22 +143,22 @@
142
143
  "@datadog/native-iast-taint-tracking": "4.1.0",
143
144
  "@datadog/native-metrics": "3.1.1",
144
145
  "@datadog/openfeature-node-server": "^1.1.0",
145
- "@datadog/pprof": "5.13.4",
146
+ "@datadog/pprof": "5.13.5",
146
147
  "@datadog/wasm-js-rewriter": "5.0.1",
147
148
  "@opentelemetry/api": ">=1.0.0 <1.10.0",
148
149
  "@opentelemetry/api-logs": "<1.0.0",
149
- "oxc-parser": "^0.115.0"
150
+ "oxc-parser": "^0.118.0"
150
151
  },
151
152
  "devDependencies": {
152
153
  "@actions/core": "^3.0.0",
153
154
  "@actions/github": "^9.0.0",
154
155
  "@babel/helpers": "^7.28.6",
155
- "@eslint/eslintrc": "^3.3.1",
156
+ "@eslint/eslintrc": "^3.3.5",
156
157
  "@eslint/js": "^9.39.2",
157
158
  "@msgpack/msgpack": "^3.1.3",
158
159
  "@openfeature/core": "^1.8.1",
159
160
  "@openfeature/server-sdk": "~1.20.0",
160
- "@stylistic/eslint-plugin": "^5.7.1",
161
+ "@stylistic/eslint-plugin": "^5.10.0",
161
162
  "@types/mocha": "^10.0.10",
162
163
  "@types/node": "^18.19.106",
163
164
  "@types/sinon": "^21.0.0",
@@ -165,11 +166,11 @@
165
166
  "benchmark": "^2.1.4",
166
167
  "body-parser": "^2.2.2",
167
168
  "bun": "1.3.10",
168
- "codeowners-audit": "^2.0.0",
169
+ "codeowners-audit": "^2.9.0",
169
170
  "eslint": "^9.39.2",
170
- "eslint-plugin-cypress": "^6.1.0",
171
+ "eslint-plugin-cypress": "^6.2.0",
171
172
  "eslint-plugin-import": "^2.32.0",
172
- "eslint-plugin-jsdoc": "^62.5.0",
173
+ "eslint-plugin-jsdoc": "^62.8.0",
173
174
  "eslint-plugin-mocha": "^11.2.0",
174
175
  "eslint-plugin-n": "^17.23.2",
175
176
  "eslint-plugin-promise": "^7.2.1",
@@ -183,9 +184,9 @@
183
184
  "mocha": "^11.6.0",
184
185
  "mocha-junit-reporter": "^2.2.1",
185
186
  "mocha-multi-reporters": "^1.5.1",
186
- "multer": "^2.0.2",
187
+ "multer": "^2.1.1",
187
188
  "nock": "^13.5.6",
188
- "nyc": "^17.1.0",
189
+ "nyc": "^18.0.0",
189
190
  "octokit": "^5.0.3",
190
191
  "opentracing": ">=0.14.7",
191
192
  "p-limit": "^7.2.0",
@@ -193,7 +194,7 @@
193
194
  "retry": "^0.13.1",
194
195
  "semifies": "^1.0.0",
195
196
  "semver": "^7.7.2",
196
- "sinon": "^21.0.1",
197
+ "sinon": "^21.0.2",
197
198
  "tiktoken": "^1.0.21",
198
199
  "typescript": "^5.9.2",
199
200
  "workerpool": "^10.0.0",
@@ -0,0 +1,75 @@
1
+ 'use strict'
2
+
3
+ const dc = require('dc-polyfill')
4
+ const shimmer = require('../../datadog-shimmer')
5
+
6
+ const {
7
+ addHook,
8
+ } = require('./helpers/instrument')
9
+
10
+ /**
11
+ * @type {import('diagnostics_channel').TracingChannel}
12
+ */
13
+ const azureDurableFunctionsChannel = dc.tracingChannel('datadog:azure:durable-functions:invoke')
14
+
15
+ addHook({ name: 'durable-functions', versions: ['>=3'], patchDefault: false }, (df) => {
16
+ const { app } = df
17
+
18
+ shimmer.wrap(app, 'entity', entityWrapper)
19
+ shimmer.wrap(app, 'activity', activityHandler)
20
+
21
+ return df
22
+ })
23
+
24
+ function entityWrapper (method) {
25
+ return function (entityName, arg) {
26
+ // because this method is overloaded, the second argument can either be an object
27
+ // with the handler or the handler itself, so first we figure which type it is
28
+ if (typeof arg === 'function') {
29
+ // if a function, this is the handler we want to wrap and trace
30
+ arguments[1] = shimmer.wrapFunction(arg, handler => entityHandler(handler, entityName))
31
+ } else {
32
+ // if an object, access the handler then trace it
33
+ shimmer.wrap(arg, 'handler', handler => entityHandler(handler, entityName))
34
+ }
35
+
36
+ return method.apply(this, arguments)
37
+ }
38
+ }
39
+
40
+ function entityHandler (handler, entityName) {
41
+ return function () {
42
+ if (!azureDurableFunctionsChannel.hasSubscribers) return handler.apply(this, arguments)
43
+
44
+ const entityContext = arguments[0]
45
+ return azureDurableFunctionsChannel.traceSync(
46
+ handler,
47
+ { trigger: 'Entity', functionName: entityName, operationName: entityContext?.df?.operationName },
48
+ this, ...arguments)
49
+ }
50
+ }
51
+
52
+ function activityHandler (method) {
53
+ return function (activityName, activityOptions) {
54
+ shimmer.wrap(activityOptions, 'handler', handler => {
55
+ const isAsync =
56
+ handler && handler.constructor && handler.constructor.name === 'AsyncFunction'
57
+
58
+ return function () {
59
+ if (!azureDurableFunctionsChannel.hasSubscribers) return handler.apply(this, arguments)
60
+
61
+ // use tracePromise if this is an async handler. otherwise, use traceSync
62
+ return isAsync
63
+ ? azureDurableFunctionsChannel.tracePromise(
64
+ handler,
65
+ { trigger: 'Activity', functionName: activityName },
66
+ this, ...arguments)
67
+ : azureDurableFunctionsChannel.traceSync(
68
+ handler,
69
+ { trigger: 'Activity', functionName: activityName },
70
+ this, ...arguments)
71
+ }
72
+ })
73
+ return method.apply(this, arguments)
74
+ }
75
+ }
@@ -55,6 +55,8 @@ const originalCoverageMap = createCoverageMap()
55
55
  const patched = new WeakSet()
56
56
 
57
57
  const lastStatusByPickleId = new Map()
58
+ /** For ATR: statuses keyed by stable scenario id (uri:name) so retries accumulate correctly */
59
+ const atrStatusesByScenarioKey = new Map()
58
60
  const numRetriesByPickleId = new Map()
59
61
  const numAttemptToCtx = new Map()
60
62
  const newTestsByTestFullname = new Map()
@@ -164,9 +166,9 @@ function getErrorFromCucumberResult (cucumberResult) {
164
166
  return error
165
167
  }
166
168
 
167
- function getChannelPromise (channelToPublishTo, isParallel = false, frameworkVersion = null) {
169
+ function getChannelPromise (channelToPublishTo, frameworkVersion = null) {
168
170
  return new Promise(resolve => {
169
- channelToPublishTo.publish({ onDone: resolve, isParallel, frameworkVersion })
171
+ channelToPublishTo.publish({ onDone: resolve, frameworkVersion })
170
172
  })
171
173
  }
172
174
 
@@ -275,6 +277,17 @@ function wrapRun (pl, isLatestVersion, version) {
275
277
  const isFirstAttempt = numAttempt++ === 0
276
278
  const isAtrRetry = !isFirstAttempt && isFlakyTestRetriesEnabled
277
279
 
280
+ // ATR: record this attempt as failed so when run().finally runs (after retry) we have all statuses
281
+ if (isFlakyTestRetriesEnabled && isAtrRetry === false) {
282
+ const nameForKey = this.pickle.name.replace(/\s*\(attempt \d+(?:, retried)?\)\s*$/, '')
283
+ const atrKey = `${this.pickle.uri}:${nameForKey}`
284
+ if (atrStatusesByScenarioKey.has(atrKey)) {
285
+ atrStatusesByScenarioKey.get(atrKey).push('fail')
286
+ } else {
287
+ atrStatusesByScenarioKey.set(atrKey, ['fail'])
288
+ }
289
+ }
290
+
278
291
  if (promises.hitBreakpointPromise) {
279
292
  await promises.hitBreakpointPromise
280
293
  }
@@ -367,6 +380,25 @@ function wrapRun (pl, isLatestVersion, version) {
367
380
  }
368
381
  }
369
382
 
383
+ // ATR: accumulate statuses by stable scenario key (uri:name) so retries are grouped.
384
+ // Cucumber appends " (attempt N)" or " (attempt N, retried)" to the scenario name; normalize for keying.
385
+ if (isFlakyTestRetriesEnabled && !isAttemptToFix && !isEfdRetry && numTestRetries > 0) {
386
+ const nameForKey = this.pickle.name.replace(/\s*\(attempt \d+(?:, retried)?\)\s*$/, '')
387
+ const atrKey = `${this.pickle.uri}:${nameForKey}`
388
+ if (atrStatusesByScenarioKey.has(atrKey)) {
389
+ atrStatusesByScenarioKey.get(atrKey).push(status)
390
+ } else {
391
+ atrStatusesByScenarioKey.set(atrKey, [status])
392
+ }
393
+ const atrStatuses = atrStatusesByScenarioKey.get(atrKey)
394
+ const pickleStatuses = lastStatusByPickleId.get(this.pickle.id)
395
+ const statusesToCheck = atrStatuses?.length >= (numTestRetries + 1) ? atrStatuses : pickleStatuses
396
+ if (statusesToCheck && statusesToCheck.length === numTestRetries + 1 &&
397
+ statusesToCheck.every(s => s === 'fail')) {
398
+ hasFailedAllRetries = true
399
+ }
400
+ }
401
+
370
402
  const attemptCtx = numAttemptToCtx.get(numAttempt)
371
403
 
372
404
  const error = getErrorFromCucumberResult(result)
@@ -473,14 +505,15 @@ function getWrappedStart (start, frameworkVersion, isParallel = false, isCoordin
473
505
  }
474
506
  let errorSkippableRequest
475
507
 
476
- const configurationResponse = await getChannelPromise(libraryConfigurationCh, isParallel, frameworkVersion)
508
+ const configurationResponse = await getChannelPromise(libraryConfigurationCh, frameworkVersion)
477
509
 
478
510
  isEarlyFlakeDetectionEnabled = configurationResponse.libraryConfig?.isEarlyFlakeDetectionEnabled
479
511
  earlyFlakeDetectionNumRetries = configurationResponse.libraryConfig?.earlyFlakeDetectionNumRetries
480
512
  earlyFlakeDetectionFaultyThreshold = configurationResponse.libraryConfig?.earlyFlakeDetectionFaultyThreshold
481
513
  isSuitesSkippingEnabled = configurationResponse.libraryConfig?.isSuitesSkippingEnabled
482
514
  isFlakyTestRetriesEnabled = configurationResponse.libraryConfig?.isFlakyTestRetriesEnabled
483
- numTestRetries = configurationResponse.libraryConfig?.flakyTestRetriesCount
515
+ const configRetryCount = configurationResponse.libraryConfig?.flakyTestRetriesCount
516
+ numTestRetries = (typeof configRetryCount === 'number' && configRetryCount > 0) ? configRetryCount : 0
484
517
  isKnownTestsEnabled = configurationResponse.libraryConfig?.isKnownTestsEnabled
485
518
  isTestManagementTestsEnabled = configurationResponse.libraryConfig?.isTestManagementEnabled
486
519
  testManagementAttemptToFixRetries = configurationResponse.libraryConfig?.testManagementAttemptToFixRetries
@@ -563,6 +596,7 @@ function getWrappedStart (start, frameworkVersion, isParallel = false, isCoordin
563
596
  options.retry = numTestRetries
564
597
  }
565
598
 
599
+ atrStatusesByScenarioKey.clear()
566
600
  sessionStartCh.publish({ command, frameworkVersion })
567
601
 
568
602
  if (!errorSkippableRequest && skippedSuites.length) {
@@ -647,6 +681,7 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
647
681
  let isQuarantined = false
648
682
  let isModified = false
649
683
 
684
+ const originalDryRun = this.options.dryRun
650
685
  if (isTestManagementTestsEnabled) {
651
686
  const testProperties = getTestProperties(testSuitePath, pickle.name)
652
687
  isAttemptToFix = testProperties.attemptToFix
@@ -685,6 +720,9 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
685
720
  // TODO: for >=11 we could use `runTestCaseResult` instead of accumulating results in `lastStatusByPickleId`
686
721
  let runTestCaseResult = await runTestCaseFunction.apply(this, arguments)
687
722
 
723
+ // Restore dryRun so it doesn't affect subsequent tests in the same worker
724
+ this.options.dryRun = originalDryRun
725
+
688
726
  const testStatuses = lastStatusByPickleId.get(pickle.id)
689
727
  const lastTestStatus = testStatuses.at(-1)
690
728
 
@@ -1016,6 +1054,15 @@ addHook({
1016
1054
  this.options.worldParameters._ddModifiedFiles = modifiedFiles
1017
1055
  }
1018
1056
 
1057
+ this.options.worldParameters._ddIsFlakyTestRetriesEnabled = isFlakyTestRetriesEnabled
1058
+ this.options.worldParameters._ddNumTestRetries = numTestRetries
1059
+
1060
+ if (isTestManagementTestsEnabled) {
1061
+ this.options.worldParameters._ddIsTestManagementTestsEnabled = true
1062
+ this.options.worldParameters._ddTestManagementTests = testManagementTests
1063
+ this.options.worldParameters._ddTestManagementAttemptToFixRetries = testManagementAttemptToFixRetries
1064
+ }
1065
+
1019
1066
  return startWorker.apply(this, arguments)
1020
1067
  })
1021
1068
  return adapterPackage
@@ -1051,6 +1098,13 @@ addHook({
1051
1098
  if (isImpactedTestsEnabled) {
1052
1099
  modifiedFiles = this.options.worldParameters._ddModifiedFiles
1053
1100
  }
1101
+ isFlakyTestRetriesEnabled = !!this.options.worldParameters._ddIsFlakyTestRetriesEnabled
1102
+ numTestRetries = this.options.worldParameters._ddNumTestRetries ?? 0
1103
+ isTestManagementTestsEnabled = !!this.options.worldParameters._ddIsTestManagementTestsEnabled
1104
+ if (isTestManagementTestsEnabled) {
1105
+ testManagementTests = this.options.worldParameters._ddTestManagementTests
1106
+ testManagementAttemptToFixRetries = this.options.worldParameters._ddTestManagementAttemptToFixRetries
1107
+ }
1054
1108
  }
1055
1109
  )
1056
1110
  return workerPackage
@@ -6,12 +6,21 @@ const {
6
6
  addHook,
7
7
  } = require('./helpers/instrument')
8
8
 
9
- addHook({ name: '@elastic/transport', file: 'lib/Transport.js', versions: ['>=8'] }, (exports) => {
10
- shimmer.wrap(exports.default.prototype, 'request', createWrapRequest('elasticsearch'))
11
- shimmer.wrap(exports.default.prototype, 'getConnection', createWrapGetConnection('elasticsearch'))
9
+ addHook({ name: '@elastic/transport', file: 'lib/Transport.js', versions: ['>=8 <9'] }, (exports) => {
10
+ wrapTransportPrototype(exports.default)
12
11
  return exports
13
12
  })
14
13
 
14
+ addHook({ name: '@elastic/transport', versions: ['>=9'] }, (exports) => {
15
+ wrapTransportPrototype(exports.Transport)
16
+ return exports
17
+ })
18
+
19
+ function wrapTransportPrototype (Transport) {
20
+ shimmer.wrap(Transport.prototype, 'request', createWrapRequest('elasticsearch'))
21
+ shimmer.wrap(Transport.prototype, 'getConnection', createWrapGetConnection('elasticsearch'))
22
+ }
23
+
15
24
  addHook({ name: '@elastic/elasticsearch', file: 'lib/Transport.js', versions: ['>=5.6.16 <8', '>=8'] }, Transport => {
16
25
  shimmer.wrap(Transport.prototype, 'request', createWrapRequest('elasticsearch'))
17
26
  shimmer.wrap(Transport.prototype, 'getConnection', createWrapGetConnection('elasticsearch'))
@@ -4,10 +4,12 @@ module.exports = {
4
4
  '@anthropic-ai/sdk': { esmFirst: true, fn: () => require('../anthropic') },
5
5
  '@apollo/server': () => require('../apollo-server'),
6
6
  '@apollo/gateway': () => require('../apollo'),
7
+ '@langchain/langgraph': { esmFirst: true, fn: () => require('../langgraph') },
7
8
  'apollo-server-core': () => require('../apollo-server-core'),
8
9
  '@aws-sdk/smithy-client': () => require('../aws-sdk'),
9
10
  '@azure/event-hubs': () => require('../azure-event-hubs'),
10
11
  '@azure/functions': () => require('../azure-functions'),
12
+ 'durable-functions': () => require('../azure-durable-functions'),
11
13
  '@azure/service-bus': () => require('../azure-service-bus'),
12
14
  '@cucumber/cucumber': () => require('../cucumber'),
13
15
  '@playwright/test': () => require('../playwright'),
@@ -4,4 +4,5 @@ module.exports = [
4
4
  ...require('./ai'),
5
5
  ...require('./bullmq'),
6
6
  ...require('./langchain'),
7
+ ...require('./langgraph'),
7
8
  ]
@@ -0,0 +1,30 @@
1
+ 'use strict'
2
+
3
+ module.exports = [
4
+ {
5
+ module: {
6
+ name: '@langchain/langgraph',
7
+ versionRange: '>=1.1.2',
8
+ filePath: 'dist/pregel/index.js',
9
+ },
10
+ functionQuery: {
11
+ methodName: 'stream',
12
+ className: 'Pregel',
13
+ kind: 'AsyncIterator',
14
+ },
15
+ channelName: 'Pregel_stream',
16
+ },
17
+ {
18
+ module: {
19
+ name: '@langchain/langgraph',
20
+ versionRange: '>=1.1.2',
21
+ filePath: 'dist/pregel/index.cjs',
22
+ },
23
+ functionQuery: {
24
+ methodName: 'stream',
25
+ className: 'Pregel',
26
+ kind: 'AsyncIterator',
27
+ },
28
+ channelName: 'Pregel_stream',
29
+ },
30
+ ]
@@ -47,6 +47,7 @@ const testSkippedCh = channel('ci:jest:test:skip')
47
47
  const testFinishCh = channel('ci:jest:test:finish')
48
48
  const testErrCh = channel('ci:jest:test:err')
49
49
  const testFnCh = channel('ci:jest:test:fn')
50
+ const testSuiteHookFnCh = channel('ci:jest:test-suite:hook:fn')
50
51
 
51
52
  const skippableSuitesCh = channel('ci:jest:test-suite:skippable')
52
53
  const libraryConfigurationCh = channel('ci:jest:library-configuration')
@@ -505,6 +506,19 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
505
506
  })
506
507
  }
507
508
 
509
+ if (event.name === 'hook_start' && (event.hook.type === 'beforeAll' || event.hook.type === 'afterAll')) {
510
+ const ctx = { testSuiteAbsolutePath: this.testSuiteAbsolutePath }
511
+ let hookFn = event.hook.fn
512
+ if (originalHookFns.has(event.hook)) {
513
+ hookFn = originalHookFns.get(event.hook)
514
+ } else {
515
+ originalHookFns.set(event.hook, hookFn)
516
+ }
517
+ event.hook.fn = shimmer.wrapFunction(hookFn, hookFn => function () {
518
+ return testSuiteHookFnCh.runStores(ctx, () => hookFn.apply(this, arguments))
519
+ })
520
+ }
521
+
508
522
  if (event.name === 'add_test') {
509
523
  if (event.failing) {
510
524
  return
@@ -673,6 +687,14 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
673
687
  }
674
688
  }
675
689
 
690
+ // ATR: set failedAllTests when all auto test retries were exhausted and every attempt failed
691
+ if (this.isFlakyTestRetriesEnabled && !isAttemptToFix && !isEfdRetry) {
692
+ const maxRetries = Number(this.global[RETRY_TIMES]) || 0
693
+ if (event.test?.invocations === maxRetries + 1 && status === 'fail') {
694
+ failedAllTests = true
695
+ }
696
+ }
697
+
676
698
  const promises = {}
677
699
  const numRetries = this.global[RETRY_TIMES]
678
700
  const numTestExecutions = event.test?.invocations
@@ -680,11 +702,15 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
680
702
  const mightHitBreakpoint = this.isDiEnabled && numTestExecutions >= 2
681
703
 
682
704
  const ctx = testContexts.get(event.test)
705
+ if (!ctx) {
706
+ log.warn('"ci:jest:test_done": no context found for test "%s"', testName)
707
+ return
708
+ }
683
709
 
684
710
  const finalStatus = this.getFinalStatus(testName,
685
711
  status,
686
- !!ctx?.isNew,
687
- !!ctx?.isModified,
712
+ !!ctx.isNew,
713
+ !!ctx.isModified,
688
714
  isEfdRetry,
689
715
  isAttemptToFix,
690
716
  numTestExecutions)
@@ -739,6 +765,9 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
739
765
  efdDeterminedRetries.clear()
740
766
  efdSlowAbortedTests.clear()
741
767
  efdNewTestCandidates.clear()
768
+ retriedTestsToNumAttempts.clear()
769
+ attemptToFixRetriedTestsStatuses.clear()
770
+ testsToBeRetried.clear()
742
771
  }
743
772
  if (event.name === 'test_skip' || event.name === 'test_todo') {
744
773
  const testName = getJestTestName(event.test, this.getShouldStripSeedFromTestName())
@@ -0,0 +1,7 @@
1
+ 'use strict'
2
+
3
+ const { addHook, getHooks } = require('./helpers/instrument')
4
+
5
+ for (const hook of getHooks('@langchain/langgraph')) {
6
+ addHook(hook, exports => exports)
7
+ }
@@ -101,12 +101,12 @@ function getFilteredSuites (originalSuites) {
101
101
  }, { suitesToRun: [], skippedSuites: new Set() })
102
102
  }
103
103
 
104
- function getOnStartHandler (isParallel, frameworkVersion) {
104
+ function getOnStartHandler (frameworkVersion) {
105
105
  return function () {
106
106
  const processArgv = process.argv.slice(2).join(' ')
107
107
  const command = `mocha ${processArgv}`
108
108
  testSessionStartCh.publish({ command, frameworkVersion })
109
- if (!isParallel && skippedSuites.length) {
109
+ if (skippedSuites.length) {
110
110
  itrSkippedSuitesCh.publish({ skippedSuites, frameworkVersion })
111
111
  }
112
112
  }
@@ -315,10 +315,9 @@ function getExecutionConfiguration (runner, isParallel, frameworkVersion, onFini
315
315
  config.isTestManagementTestsEnabled = libraryConfig.isTestManagementEnabled
316
316
  config.testManagementAttemptToFixRetries = libraryConfig.testManagementAttemptToFixRetries
317
317
  config.isImpactedTestsEnabled = libraryConfig.isImpactedTestsEnabled
318
- // ITR and auto test retries are not supported in parallel mode yet
319
- config.isSuitesSkippingEnabled = !isParallel && libraryConfig.isSuitesSkippingEnabled
320
- config.isFlakyTestRetriesEnabled = !isParallel && libraryConfig.isFlakyTestRetriesEnabled
321
- config.flakyTestRetriesCount = !isParallel && libraryConfig.flakyTestRetriesCount
318
+ config.isSuitesSkippingEnabled = libraryConfig.isSuitesSkippingEnabled
319
+ config.isFlakyTestRetriesEnabled = libraryConfig.isFlakyTestRetriesEnabled
320
+ config.flakyTestRetriesCount = libraryConfig.flakyTestRetriesCount
322
321
 
323
322
  if (config.isKnownTestsEnabled) {
324
323
  ctx.onDone = onReceivedKnownTests
@@ -452,7 +451,7 @@ addHook({
452
451
 
453
452
  const { suitesByTestFile, numSuitesByTestFile } = getSuitesByTestFile(this.suite)
454
453
 
455
- this.once('start', getOnStartHandler(false, frameworkVersion))
454
+ this.once('start', getOnStartHandler(frameworkVersion))
456
455
 
457
456
  this.once('end', getOnEndHandler(false))
458
457
 
@@ -623,9 +622,16 @@ addHook({
623
622
  return run.apply(this, arguments)
624
623
  }
625
624
 
626
- this.once('start', getOnStartHandler(true, frameworkVersion))
625
+ this.once('start', getOnStartHandler(frameworkVersion))
627
626
  this.once('end', getOnEndHandler(true))
628
627
 
628
+ // Populate unskippable suites before config is fetched (matches serial mode at Mocha.prototype.run)
629
+ for (const filePath of files) {
630
+ if (isMarkedAsUnskippable({ path: filePath })) {
631
+ unskippableSuites.push(filePath)
632
+ }
633
+ }
634
+
629
635
  getExecutionConfiguration(this, true, frameworkVersion, () => {
630
636
  if (config.isKnownTestsEnabled) {
631
637
  const testSuites = files.map(file => getTestSuitePath(file, process.cwd()))
@@ -640,7 +646,25 @@ addHook({
640
646
  config.isEarlyFlakeDetectionFaulty = true
641
647
  }
642
648
  }
643
- run.apply(this, arguments)
649
+ if (config.isSuitesSkippingEnabled && suitesToSkip.length) {
650
+ const filteredFiles = []
651
+ const skippedFiles = []
652
+ for (const file of files) {
653
+ const testPath = getTestSuitePath(file, process.cwd())
654
+ const shouldSkip = suitesToSkip.includes(testPath)
655
+ const isUnskippable = unskippableSuites.includes(file)
656
+ if (shouldSkip && !isUnskippable) {
657
+ skippedFiles.push(testPath)
658
+ } else {
659
+ filteredFiles.push(file)
660
+ }
661
+ }
662
+ isSuitesSkipped = skippedFiles.length > 0
663
+ skippedSuites = skippedFiles
664
+ run.apply(this, [cb, { files: filteredFiles }])
665
+ } else {
666
+ run.apply(this, arguments)
667
+ }
644
668
  })
645
669
 
646
670
  return this
@@ -663,7 +687,8 @@ addHook({
663
687
  if (!testFinishCh.hasSubscribers ||
664
688
  (!config.isKnownTestsEnabled &&
665
689
  !config.isTestManagementTestsEnabled &&
666
- !config.isImpactedTestsEnabled)) {
690
+ !config.isImpactedTestsEnabled &&
691
+ !config.isFlakyTestRetriesEnabled)) {
667
692
  return run.apply(this, arguments)
668
693
  }
669
694
 
@@ -693,8 +718,7 @@ addHook({
693
718
  if (config.isTestManagementTestsEnabled) {
694
719
  const testSuiteTestManagementTests = config.testManagementTests?.mocha?.suites?.[testPath] || {}
695
720
  newWorkerArgs._ddIsTestManagementTestsEnabled = true
696
- // TODO: attempt to fix does not work in parallel mode yet
697
- // newWorkerArgs._ddTestManagementAttemptToFixRetries = config.testManagementAttemptToFixRetries
721
+ newWorkerArgs._ddTestManagementAttemptToFixRetries = config.testManagementAttemptToFixRetries
698
722
  newWorkerArgs._ddTestManagementTests = {
699
723
  mocha: {
700
724
  suites: {
@@ -709,6 +733,11 @@ addHook({
709
733
  newWorkerArgs._ddModifiedFiles = config.modifiedFiles || {}
710
734
  }
711
735
 
736
+ if (config.isFlakyTestRetriesEnabled) {
737
+ newWorkerArgs._ddIsFlakyTestRetriesEnabled = true
738
+ newWorkerArgs._ddFlakyTestRetriesCount = config.flakyTestRetriesCount
739
+ }
740
+
712
741
  // We pass the known tests for the test file to the worker
713
742
  const testFileResult = await run.apply(
714
743
  this,