dd-trace 5.121.0 → 5.122.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 +0 -17
- package/index.d.ts +108 -3
- package/initialize.mjs +28 -17
- package/openfeature.js +2 -2
- package/package.json +11 -8
- package/packages/datadog-esbuild/index.js +0 -34
- package/packages/datadog-instrumentations/src/anthropic.js +210 -15
- package/packages/datadog-instrumentations/src/aws-sdk.js +9 -1
- package/packages/datadog-instrumentations/src/browser-bunyan.js +11 -0
- package/packages/datadog-instrumentations/src/bunyan.js +3 -16
- package/packages/datadog-instrumentations/src/cucumber.js +26 -11
- package/packages/datadog-instrumentations/src/cypress-config.js +182 -16
- package/packages/datadog-instrumentations/src/cypress-legacy-finalizers.js +30 -0
- package/packages/datadog-instrumentations/src/cypress.js +2 -0
- package/packages/datadog-instrumentations/src/fastify.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/bunyan.js +22 -0
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -1
- package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +9 -4
- package/packages/datadog-instrumentations/src/helpers/register.js +10 -8
- package/packages/datadog-instrumentations/src/jest.js +25 -17
- package/packages/datadog-instrumentations/src/mariadb.js +278 -21
- package/packages/datadog-instrumentations/src/mocha/main.js +539 -55
- package/packages/datadog-instrumentations/src/mocha/utils.js +130 -17
- package/packages/datadog-instrumentations/src/playwright-reporter.js +99 -0
- package/packages/datadog-instrumentations/src/playwright.js +149 -2
- package/packages/datadog-instrumentations/src/router.js +2 -0
- package/packages/datadog-instrumentations/src/vitest-main.js +11 -3
- package/packages/datadog-plugin-aws-sdk/src/base.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +163 -44
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +2 -8
- package/packages/datadog-plugin-azure-functions/src/index.js +1 -1
- package/packages/datadog-plugin-browser-bunyan/src/index.js +9 -0
- package/packages/datadog-plugin-bunyan/src/index.js +5 -8
- package/packages/datadog-plugin-cucumber/src/index.js +11 -0
- package/packages/datadog-plugin-cypress/src/after-run.js +5 -1
- package/packages/datadog-plugin-cypress/src/after-spec.js +5 -1
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +81 -29
- package/packages/datadog-plugin-cypress/src/finalization.js +76 -0
- package/packages/datadog-plugin-cypress/src/index.js +12 -6
- package/packages/datadog-plugin-cypress/src/plugin.js +16 -3
- package/packages/datadog-plugin-dd-trace-api/src/index.js +4 -0
- package/packages/datadog-plugin-electron/src/index.js +2 -0
- package/packages/datadog-plugin-fs/src/index.js +1 -1
- package/packages/datadog-plugin-jest/src/index.js +6 -0
- package/packages/datadog-plugin-mocha/src/index.js +56 -1
- package/packages/datadog-plugin-nats/src/index.js +1 -1
- package/packages/datadog-plugin-openai/src/tracing.js +41 -30
- package/packages/datadog-plugin-openai-agents/src/integration.js +1 -0
- package/packages/datadog-plugin-playwright/src/index.js +42 -13
- package/packages/datadog-plugin-vitest/src/index.js +6 -0
- package/packages/datadog-webpack/index.js +0 -11
- package/packages/dd-trace/index.js +0 -1
- package/packages/dd-trace/src/agent/info.js +6 -5
- package/packages/dd-trace/src/aiguard/client.js +100 -1
- package/packages/dd-trace/src/aiguard/errors.js +41 -0
- package/packages/dd-trace/src/aiguard/evaluation.js +357 -0
- package/packages/dd-trace/src/aiguard/integrations/anthropic.js +70 -0
- package/packages/dd-trace/src/aiguard/integrations/index.js +4 -0
- package/packages/dd-trace/src/aiguard/messages/anthropic.js +423 -0
- package/packages/dd-trace/src/aiguard/messages/openai.js +1 -24
- package/packages/dd-trace/src/aiguard/messages/utils.js +28 -0
- package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +1 -11
- package/packages/dd-trace/src/aiguard/sdk.js +22 -278
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +9 -12
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +1 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +27 -3
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +22 -6
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +21 -5
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +164 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +22 -6
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +365 -32
- package/packages/dd-trace/src/ci-visibility/exporters/request.js +201 -0
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +5 -3
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +9 -5
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +2 -1
- package/packages/dd-trace/src/ci-visibility/requests/rate-limit.js +39 -0
- package/packages/dd-trace/src/ci-visibility/requests/request.js +3 -6
- package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +6 -2
- package/packages/dd-trace/src/config/generated-config-types.d.ts +4 -0
- package/packages/dd-trace/src/config/index.js +13 -2
- package/packages/dd-trace/src/config/major-overrides.js +7 -0
- package/packages/dd-trace/src/config/supported-configurations.json +14 -0
- package/packages/dd-trace/src/datastreams/index.js +2 -0
- package/packages/dd-trace/src/datastreams/manager.js +9 -2
- package/packages/dd-trace/src/datastreams/processor.js +15 -13
- package/packages/dd-trace/src/datastreams/size.js +5 -0
- package/packages/dd-trace/src/debugger/devtools_client/index.js +3 -2
- package/packages/dd-trace/src/exporters/agent/writer.js +49 -9
- package/packages/dd-trace/src/exporters/common/buffering-exporter.js +6 -4
- package/packages/dd-trace/src/exporters/common/writer.js +11 -6
- package/packages/dd-trace/src/llmobs/experiments/client.js +163 -5
- package/packages/dd-trace/src/llmobs/experiments/dataset.js +241 -87
- package/packages/dd-trace/src/llmobs/experiments/experiment.js +280 -20
- package/packages/dd-trace/src/llmobs/experiments/index.js +58 -59
- package/packages/dd-trace/src/llmobs/experiments/noop.js +71 -3
- package/packages/dd-trace/src/llmobs/experiments/result.js +1 -0
- package/packages/dd-trace/src/llmobs/experiments/util.js +41 -5
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +12 -3
- package/packages/dd-trace/src/llmobs/plugins/base.js +4 -2
- package/packages/dd-trace/src/noop/proxy.js +3 -4
- package/packages/dd-trace/src/openfeature/flagging_provider.js +18 -2
- package/packages/dd-trace/src/openfeature/remote_config.js +1 -1
- package/packages/dd-trace/src/opentelemetry/context_manager.js +7 -7
- package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +2 -1
- package/packages/dd-trace/src/opentelemetry/logs/logger.js +67 -11
- package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +14 -3
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +25 -12
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +2 -5
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +96 -43
- package/packages/dd-trace/src/opentracing/tracer.js +5 -2
- package/packages/dd-trace/src/otel-thread-ctx.js +387 -0
- package/packages/dd-trace/src/plugin_manager.js +3 -3
- package/packages/dd-trace/src/plugins/ci_plugin.js +6 -1
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/util/jest.js +4 -3
- package/packages/dd-trace/src/plugins/util/test.js +9 -9
- package/packages/dd-trace/src/plugins/util/web.js +3 -0
- package/packages/dd-trace/src/profiling/profiler.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/space.js +1 -3
- package/packages/dd-trace/src/profiling/profilers/wall.js +37 -90
- package/packages/dd-trace/src/profiling/webspan-utils.js +45 -0
- package/packages/dd-trace/src/proxy.js +38 -29
- package/packages/dd-trace/src/ritm.js +9 -2
- package/packages/dd-trace/src/serverless.js +37 -0
- package/packages/dd-trace/src/span_format.js +1 -0
- package/packages/dd-trace/src/span_stats.js +30 -3
- package/packages/dd-trace/src/storage-channels.js +86 -0
- package/packages/dd-trace/src/tracer.js +9 -2
- package/packages/dd-trace/src/tracer_metadata.js +14 -1
- package/packages/dd-trace/src/web-tags-cache.js +132 -0
- package/vendor/dist/@datadog/openfeature-node-server/LICENSE +201 -0
- package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +0 -65
- package/packages/datadog-webpack/src/optional-peer-loader.js +0 -17
- package/packages/dd-trace/src/feature-registry.js +0 -29
- package/packages/dd-trace/src/openfeature/register.js +0 -35
- package/packages/dd-trace/src/openfeature/require-provider.js +0 -18
|
@@ -1,35 +1,90 @@
|
|
|
1
1
|
'use strict'
|
|
2
|
+
|
|
3
|
+
const { DsmPathwayCodec, PATHWAY_FIELD_BYTES } = require('../../../dd-trace/src/datastreams')
|
|
2
4
|
const log = require('../../../dd-trace/src/log')
|
|
3
5
|
const BaseAwsSdkPlugin = require('../base')
|
|
4
6
|
|
|
7
|
+
const DEFAULT_EVENT_BUS = 'default'
|
|
8
|
+
const DEFAULT_DETAIL_TYPE = 'unknown'
|
|
5
9
|
// EventBridge enforces this limit over the whole PutEvents request (the sum of
|
|
6
|
-
// every entry), not over a single entry.
|
|
10
|
+
// every entry), not over a single entry.
|
|
7
11
|
const MAX_PUT_EVENTS_BYTES = 1024 * 1024
|
|
12
|
+
// Sizing runs before any checkpoint exists, so entries that carry nothing but a pathway are
|
|
13
|
+
// measured against this stand-in.
|
|
14
|
+
const PATHWAY_SIZE_PROBE = { hash: Buffer.alloc(8), pathwayStartNs: 0, edgeStartNs: 0 }
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @typedef {object} PutEventsRequestEntry
|
|
18
|
+
* @property {string} [Detail]
|
|
19
|
+
* @property {string} [DetailType]
|
|
20
|
+
* @property {string} [EventBusName]
|
|
21
|
+
* @property {string[]} [Resources]
|
|
22
|
+
* @property {string} [Source]
|
|
23
|
+
* @property {Date} [Time]
|
|
24
|
+
* @property {string} [TraceHeader]
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Size a single entry the way DSM reports payloads across tracers: the UTF-8 byte length of every
|
|
29
|
+
* string field the caller supplied. `Time` is excluded because it is not part of the message the
|
|
30
|
+
* consumer receives, which is also why this differs from `putEventEntrySize`.
|
|
31
|
+
*
|
|
32
|
+
* @param {PutEventsRequestEntry} entry whose `Detail` the caller has already checked is a string.
|
|
33
|
+
* @returns {number}
|
|
34
|
+
*/
|
|
35
|
+
function dsmPayloadSize (entry) {
|
|
36
|
+
let size = Buffer.byteLength(entry.Detail)
|
|
37
|
+
if (typeof entry.DetailType === 'string') size += Buffer.byteLength(entry.DetailType)
|
|
38
|
+
if (typeof entry.EventBusName === 'string') size += Buffer.byteLength(entry.EventBusName)
|
|
39
|
+
if (typeof entry.Source === 'string') size += Buffer.byteLength(entry.Source)
|
|
40
|
+
if (typeof entry.TraceHeader === 'string') size += Buffer.byteLength(entry.TraceHeader)
|
|
41
|
+
if (Array.isArray(entry.Resources)) {
|
|
42
|
+
for (const resource of entry.Resources) {
|
|
43
|
+
if (typeof resource === 'string') size += Buffer.byteLength(resource)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return size
|
|
47
|
+
}
|
|
8
48
|
|
|
9
49
|
/**
|
|
10
50
|
* Size a single `PutEventsRequestEntry` the way EventBridge does server-side:
|
|
11
51
|
* the UTF-8 byte length of `Source`, `DetailType`, `Detail`, and each
|
|
12
52
|
* `Resources` ARN, plus a flat 14 bytes when `Time` is set.
|
|
13
53
|
*
|
|
14
|
-
* @param {
|
|
54
|
+
* @param {PutEventsRequestEntry} entry
|
|
15
55
|
* @param {string} [detail] overrides `entry.Detail`, used to size the entry as
|
|
16
56
|
* it would be sent with the injected `_datadog` context
|
|
17
|
-
* @returns {number}
|
|
18
57
|
* @see https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-putevents.html
|
|
19
58
|
*/
|
|
20
|
-
function putEventEntrySize (entry, detail
|
|
59
|
+
function putEventEntrySize (entry, detail) {
|
|
60
|
+
if (entry == null) return 0
|
|
61
|
+
|
|
62
|
+
detail ??= entry.Detail
|
|
21
63
|
let size = entry.Time == null ? 0 : 14
|
|
22
|
-
if (entry.Source
|
|
23
|
-
if (entry.DetailType
|
|
24
|
-
if (detail
|
|
25
|
-
if (entry.Resources
|
|
64
|
+
if (typeof entry.Source === 'string') size += Buffer.byteLength(entry.Source)
|
|
65
|
+
if (typeof entry.DetailType === 'string') size += Buffer.byteLength(entry.DetailType)
|
|
66
|
+
if (typeof detail === 'string') size += Buffer.byteLength(detail)
|
|
67
|
+
if (Array.isArray(entry.Resources)) {
|
|
26
68
|
for (const resource of entry.Resources) {
|
|
27
|
-
if (resource
|
|
69
|
+
if (typeof resource === 'string') size += Buffer.byteLength(resource)
|
|
28
70
|
}
|
|
29
71
|
}
|
|
30
72
|
return size
|
|
31
73
|
}
|
|
32
74
|
|
|
75
|
+
/**
|
|
76
|
+
* @param {string} detail
|
|
77
|
+
* @param {Record<string, string>} carrier
|
|
78
|
+
* @returns {string|undefined} `undefined` when `detail` is not a JSON object
|
|
79
|
+
*/
|
|
80
|
+
function injectDetail (detail, carrier) {
|
|
81
|
+
try {
|
|
82
|
+
return BaseAwsSdkPlugin.injectFieldIntoJsonObject(detail, '_datadog', carrier)
|
|
83
|
+
} catch (error) {
|
|
84
|
+
log.error('EventBridge error injecting request', error)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
33
88
|
class EventBridge extends BaseAwsSdkPlugin {
|
|
34
89
|
static id = 'eventbridge'
|
|
35
90
|
static isPayloadReporter = true
|
|
@@ -46,44 +101,31 @@ class EventBridge extends BaseAwsSdkPlugin {
|
|
|
46
101
|
}
|
|
47
102
|
|
|
48
103
|
/**
|
|
49
|
-
*
|
|
50
|
-
* @param {import('../../../..').Span} span
|
|
51
|
-
* @param {object} request
|
|
104
|
+
* The context goes into `Detail` because EventBridge reserves an entry's `TraceHeader` for X-Ray.
|
|
52
105
|
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* (https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-putevent-size.html)
|
|
106
|
+
* @param {import('../../../..').Span} span
|
|
107
|
+
* @param {{ operation: string, params?: { Entries?: PutEventsRequestEntry[] } }} request
|
|
108
|
+
* @see https://docs.aws.amazon.com/eventbridge/latest/APIReference/API_PutEventsRequestEntry.html
|
|
109
|
+
* @see https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-putevent-size.html
|
|
58
110
|
*/
|
|
59
111
|
requestInject (span, request) {
|
|
60
|
-
const operation = request
|
|
61
|
-
if (operation
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
this.tracer.inject(span, 'text_map', injected)
|
|
69
|
-
|
|
70
|
-
// Only `injectFieldIntoJsonObject` can throw (the slow path
|
|
71
|
-
// `JSON.parse` for non-`{...}` payloads). Tighten the catch around
|
|
72
|
-
// it so the rest of the body stays in V8's optimisable surface.
|
|
73
|
-
let finalData
|
|
74
|
-
try {
|
|
75
|
-
finalData = BaseAwsSdkPlugin.injectFieldIntoJsonObject(entries[0].Detail, '_datadog', injected)
|
|
76
|
-
} catch (error) {
|
|
77
|
-
log.error('EventBridge error injecting request', error)
|
|
78
|
-
return
|
|
79
|
-
}
|
|
112
|
+
const { operation, params } = request
|
|
113
|
+
if (operation !== 'putEvents' || !params?.Entries?.length) return
|
|
114
|
+
|
|
115
|
+
const entries = params.Entries
|
|
116
|
+
const dsmEnabled = this.config.dsmEnabled
|
|
117
|
+
const batchPropagationEnabled = this.config.batchPropagationEnabled
|
|
118
|
+
const traceCarrier = this.tracer.inject(span, 'text_map')
|
|
119
|
+
if (traceCarrier === undefined && !dsmEnabled) return
|
|
80
120
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
121
|
+
// The default configuration injects the first entry alone and records no checkpoint, so it
|
|
122
|
+
// needs none of the deferred-write bookkeeping the paths below depend on.
|
|
123
|
+
if (!dsmEnabled && !batchPropagationEnabled) {
|
|
124
|
+
const entry = entries[0]
|
|
125
|
+
if (typeof entry?.Detail !== 'string') return
|
|
126
|
+
const detail = injectDetail(entry.Detail, traceCarrier)
|
|
127
|
+
if (detail === undefined) return
|
|
128
|
+
let requestSize = putEventEntrySize(entry, detail)
|
|
87
129
|
for (let i = 1; requestSize < MAX_PUT_EVENTS_BYTES && i < entries.length; i++) {
|
|
88
130
|
requestSize += putEventEntrySize(entries[i])
|
|
89
131
|
}
|
|
@@ -91,8 +133,85 @@ class EventBridge extends BaseAwsSdkPlugin {
|
|
|
91
133
|
log.info('Payload size too large to pass context')
|
|
92
134
|
return
|
|
93
135
|
}
|
|
94
|
-
|
|
136
|
+
entry.Detail = detail
|
|
137
|
+
return
|
|
95
138
|
}
|
|
139
|
+
|
|
140
|
+
// Both carriers are reused across entries; the write pass overwrites the pathway on each one
|
|
141
|
+
// immediately before serializing it, so nothing may come between the two.
|
|
142
|
+
let pathwayOnlyCarrier
|
|
143
|
+
const injectable = []
|
|
144
|
+
let requestSize = 0
|
|
145
|
+
|
|
146
|
+
for (let i = 0; i < entries.length && requestSize < MAX_PUT_EVENTS_BYTES; i++) {
|
|
147
|
+
const entry = entries[i]
|
|
148
|
+
const tracePropagated = traceCarrier !== undefined && (i === 0 || batchPropagationEnabled)
|
|
149
|
+
let carrier = traceCarrier
|
|
150
|
+
if (!tracePropagated) {
|
|
151
|
+
if (dsmEnabled) pathwayOnlyCarrier ??= DsmPathwayCodec.encode(PATHWAY_SIZE_PROBE)
|
|
152
|
+
carrier = pathwayOnlyCarrier
|
|
153
|
+
}
|
|
154
|
+
if (carrier !== undefined && typeof entry?.Detail === 'string') {
|
|
155
|
+
// A malformed Detail is skipped rather than fatal: EventBridge partial-fails that entry,
|
|
156
|
+
// so the rest of the batch still propagates.
|
|
157
|
+
const traceOnlyDetail = injectDetail(entry.Detail, carrier)
|
|
158
|
+
if (traceOnlyDetail !== undefined) {
|
|
159
|
+
// The pathway field is a fixed width, so reserving it is exact and saves building the
|
|
160
|
+
// detail a second time just to measure it.
|
|
161
|
+
const reserved = dsmEnabled && tracePropagated ? PATHWAY_FIELD_BYTES : 0
|
|
162
|
+
requestSize += putEventEntrySize(entry, traceOnlyDetail) + reserved
|
|
163
|
+
injectable.push({
|
|
164
|
+
entry,
|
|
165
|
+
carrier,
|
|
166
|
+
payloadSize: dsmEnabled ? dsmPayloadSize(entry) : 0,
|
|
167
|
+
traceOnlyDetail: tracePropagated ? traceOnlyDetail : undefined,
|
|
168
|
+
})
|
|
169
|
+
continue
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
requestSize += putEventEntrySize(entry)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (injectable.length === 0) return
|
|
176
|
+
if (requestSize >= MAX_PUT_EVENTS_BYTES) {
|
|
177
|
+
log.info('Payload size too large to pass context')
|
|
178
|
+
return
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
for (const { entry, carrier, payloadSize, traceOnlyDetail } of injectable) {
|
|
182
|
+
let detail = traceOnlyDetail
|
|
183
|
+
if (dsmEnabled) {
|
|
184
|
+
const dataStreamsContext = this.setDSMCheckpoint(span, entry, payloadSize)
|
|
185
|
+
// Anything short of a real pathway leaves the carrier on the sizing stand-in, which must
|
|
186
|
+
// never ship; a pathway-only entry then has no fallback and keeps the caller's detail.
|
|
187
|
+
if (DsmPathwayCodec.encode(dataStreamsContext, carrier) !== undefined) {
|
|
188
|
+
detail = injectDetail(entry.Detail, carrier)
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (detail !== undefined) entry.Detail = detail
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Edge tags feed the pathway hash, so they are a cross-tracer contract: renaming or merging one
|
|
197
|
+
* splits the DSM pathway instead of extending it.
|
|
198
|
+
*
|
|
199
|
+
* @param {import('../../../..').Span} span
|
|
200
|
+
* @param {PutEventsRequestEntry} entry
|
|
201
|
+
* @param {number} payloadSize `dsmPayloadSize(entry)`, taken before the `_datadog` context is
|
|
202
|
+
* injected. The trailing `0` opts out of the pathway estimate the processor adds by default,
|
|
203
|
+
* because that context is propagation overhead rather than the caller's payload.
|
|
204
|
+
* @returns {object|undefined}
|
|
205
|
+
*/
|
|
206
|
+
setDSMCheckpoint (span, entry, payloadSize) {
|
|
207
|
+
const eventBus = entry.EventBusName || DEFAULT_EVENT_BUS
|
|
208
|
+
const detailType = entry.DetailType ?? DEFAULT_DETAIL_TYPE
|
|
209
|
+
return this.tracer.setCheckpoint(
|
|
210
|
+
['direction:out', `exchange:${eventBus}`, `topic:${detailType}`, 'type:eventbridge'],
|
|
211
|
+
span,
|
|
212
|
+
payloadSize,
|
|
213
|
+
0,
|
|
214
|
+
)
|
|
96
215
|
}
|
|
97
216
|
}
|
|
98
217
|
module.exports = EventBridge
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
'use strict'
|
|
2
|
-
const { DsmPathwayCodec, getSizeOrZero } = require('../../../dd-trace/src/datastreams')
|
|
2
|
+
const { DsmPathwayCodec, getSizeOrZero, PATHWAY_FIELD_BYTES } = require('../../../dd-trace/src/datastreams')
|
|
3
3
|
const log = require('../../../dd-trace/src/log')
|
|
4
4
|
const BaseAwsSdkPlugin = require('../base')
|
|
5
5
|
|
|
@@ -16,12 +16,6 @@ const MAX_TRACKED_SHARD_ITERATORS = 1000
|
|
|
16
16
|
// conservatively skip propagation above this point because the request does not expose that limit.
|
|
17
17
|
const KINESIS_DEFAULT_MAX_RECORD_BYTES = 1_048_576
|
|
18
18
|
|
|
19
|
-
// The DSM pathway field (`dd-pathway-ctx-base64`) always serializes to a fixed 55 bytes: a
|
|
20
|
-
// 21-char key, a 28-char base64 value, and 6 bytes of JSON framing. Mirrors PATHWAY_HEADER_BYTES
|
|
21
|
-
// in dd-trace/src/datastreams/processor.js. Reserved in the size gate so setDSMCheckpoint never
|
|
22
|
-
// runs for a record that would ship over the cap once the pathway context is attached.
|
|
23
|
-
const DSM_PATHWAY_FIELD_BYTES = 55
|
|
24
|
-
|
|
25
19
|
class Kinesis extends BaseAwsSdkPlugin {
|
|
26
20
|
static id = 'kinesis'
|
|
27
21
|
static peerServicePrecursors = ['streamname']
|
|
@@ -276,7 +270,7 @@ class Kinesis extends BaseAwsSdkPlugin {
|
|
|
276
270
|
// Gate before setDSMCheckpoint: a record we can't ship must not record a checkpoint.
|
|
277
271
|
// Reserve the pathway field that DSM appends after the gate.
|
|
278
272
|
let serialized = JSON.stringify(parsedData)
|
|
279
|
-
const reservedBytes = dsmEnabled ?
|
|
273
|
+
const reservedBytes = dsmEnabled ? PATHWAY_FIELD_BYTES : 0
|
|
280
274
|
const partitionKeyBytes = Buffer.byteLength(params.PartitionKey ?? '', 'utf8')
|
|
281
275
|
if (
|
|
282
276
|
Buffer.byteLength(serialized, 'utf8') + partitionKeyBytes + reservedBytes >
|
|
@@ -156,7 +156,7 @@ function setSpanLinks (triggerType, tracer, span, ctx) {
|
|
|
156
156
|
: triggerMetadata.propertiesArray
|
|
157
157
|
|
|
158
158
|
const addLinkFromProperties = (props) => {
|
|
159
|
-
if (!props
|
|
159
|
+
if (!props) return
|
|
160
160
|
const spanContext = tracer.extract('text_map', props)
|
|
161
161
|
if (spanContext) {
|
|
162
162
|
span.addLink({ context: spanContext })
|
|
@@ -8,17 +8,14 @@ class BunyanPlugin extends LogPlugin {
|
|
|
8
8
|
|
|
9
9
|
constructor (...args) {
|
|
10
10
|
super(...args)
|
|
11
|
-
this.addSub(
|
|
11
|
+
this.addSub(`apm:${this.constructor.id}:log`, (arg) => this.handleLog(arg))
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* Inject `dd` directly on the record
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* prototype, and is never the caller's input directly. Mutating it adds
|
|
20
|
-
* `dd` for every consumer (JSON streams via `JSON.stringify(rec)`, raw
|
|
21
|
-
* streams via the record reference) without paying for a Proxy view.
|
|
15
|
+
* Inject `dd` directly on the record Bunyan implementations hand us. They build the record
|
|
16
|
+
* inside `mkRecord` from a copy of the logger and caller fields, so the `rec` object that flows
|
|
17
|
+
* through `_emit` is always logger-owned. Mutating it adds `dd` for every consumer without
|
|
18
|
+
* paying for a Proxy view.
|
|
22
19
|
*
|
|
23
20
|
* @param {{ message: object }} arg
|
|
24
21
|
*/
|
|
@@ -72,6 +72,7 @@ class CucumberPlugin extends CiPlugin {
|
|
|
72
72
|
isEarlyFlakeDetectionFaulty,
|
|
73
73
|
isTestManagementTestsEnabled,
|
|
74
74
|
isParallel,
|
|
75
|
+
error,
|
|
75
76
|
onDone,
|
|
76
77
|
}) => {
|
|
77
78
|
this._exportPendingWorkerTraces()
|
|
@@ -115,6 +116,15 @@ class CucumberPlugin extends CiPlugin {
|
|
|
115
116
|
|
|
116
117
|
this.testSessionSpan.setTag(TEST_STATUS, status)
|
|
117
118
|
this.testModuleSpan.setTag(TEST_STATUS, status)
|
|
119
|
+
if (error) {
|
|
120
|
+
for (const testSuiteSpan of this._testSuiteSpansByTestSuite.values()) {
|
|
121
|
+
testSuiteSpan.setTag(TEST_STATUS, 'fail')
|
|
122
|
+
testSuiteSpan.setTag('error', error)
|
|
123
|
+
}
|
|
124
|
+
this.testSessionSpan.setTag('error', error)
|
|
125
|
+
this.testModuleSpan.setTag('error', error)
|
|
126
|
+
}
|
|
127
|
+
this.tracer._exporter.exportDeferredTestSuiteSpans?.()
|
|
118
128
|
this.testModuleSpan.finish()
|
|
119
129
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'module')
|
|
120
130
|
this.testSessionSpan.finish()
|
|
@@ -192,6 +202,7 @@ class CucumberPlugin extends CiPlugin {
|
|
|
192
202
|
this.addSub('ci:cucumber:test-suite:finish', ({ status, testSuitePath }) => {
|
|
193
203
|
const testSuiteSpan = this._testSuiteSpansByTestSuite.get(testSuitePath)
|
|
194
204
|
testSuiteSpan.setTag(TEST_STATUS, status)
|
|
205
|
+
this.tracer._exporter.deferTestSuiteSpan?.(testSuiteSpan)
|
|
195
206
|
testSuiteSpan.finish()
|
|
196
207
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'suite')
|
|
197
208
|
})
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const cypressPlugin = require('./cypress-plugin')
|
|
4
|
+
const { shouldDeferLegacyFinalization } = require('./finalization')
|
|
4
5
|
|
|
5
|
-
module.exports =
|
|
6
|
+
module.exports = function afterRun () {
|
|
7
|
+
if (shouldDeferLegacyFinalization()) return
|
|
8
|
+
return cypressPlugin.afterRun.apply(cypressPlugin, arguments)
|
|
9
|
+
}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const cypressPlugin = require('./cypress-plugin')
|
|
4
|
+
const { shouldDeferLegacyFinalization } = require('./finalization')
|
|
4
5
|
|
|
5
|
-
module.exports =
|
|
6
|
+
module.exports = function afterSpec () {
|
|
7
|
+
if (shouldDeferLegacyFinalization()) return
|
|
8
|
+
return cypressPlugin.afterSpec.apply(cypressPlugin, arguments)
|
|
9
|
+
}
|
|
@@ -477,7 +477,7 @@ class CypressPlugin {
|
|
|
477
477
|
testsToSkip = []
|
|
478
478
|
skippedTests = []
|
|
479
479
|
skippedTestIds = new Set()
|
|
480
|
-
skippableTestsCoverage
|
|
480
|
+
skippableTestsCoverage
|
|
481
481
|
testSessionCoverageMap = createCoverageMap()
|
|
482
482
|
hasForcedToRunSuites = false
|
|
483
483
|
hasUnskippableSuites = false
|
|
@@ -492,6 +492,7 @@ class CypressPlugin {
|
|
|
492
492
|
loggedAttemptToFixTests = new Set()
|
|
493
493
|
uploadedScreenshotPaths = new Set()
|
|
494
494
|
screenshotUploadPromisesByTraceId = new Map()
|
|
495
|
+
screenshotUploadAbortControllers = new Set()
|
|
495
496
|
afterScreenshotHandler = undefined
|
|
496
497
|
lastFinishedTest = null
|
|
497
498
|
pendingScreenshotUploads = []
|
|
@@ -565,7 +566,7 @@ class CypressPlugin {
|
|
|
565
566
|
this.testsToSkip = []
|
|
566
567
|
this.skippedTests = []
|
|
567
568
|
this.skippedTestIds = new Set()
|
|
568
|
-
this.skippableTestsCoverage =
|
|
569
|
+
this.skippableTestsCoverage = undefined
|
|
569
570
|
this.testSessionCoverageMap = createCoverageMap()
|
|
570
571
|
this.hasForcedToRunSuites = false
|
|
571
572
|
this.hasUnskippableSuites = false
|
|
@@ -581,10 +582,12 @@ class CypressPlugin {
|
|
|
581
582
|
this.loggedAttemptToFixTests = new Set()
|
|
582
583
|
this.uploadedScreenshotPaths = new Set()
|
|
583
584
|
this.screenshotUploadPromisesByTraceId = new Map()
|
|
585
|
+
this.screenshotUploadAbortControllers = new Set()
|
|
584
586
|
this.lastFinishedTest = null
|
|
585
587
|
this.pendingScreenshotUploads = []
|
|
586
588
|
this.activeTestSpan = null
|
|
587
589
|
this.testSuiteSpan = null
|
|
590
|
+
this.finishedTestSuiteSpans = []
|
|
588
591
|
this.testModuleSpan = null
|
|
589
592
|
this.testSessionSpan = null
|
|
590
593
|
this.command = undefined
|
|
@@ -643,6 +646,19 @@ class CypressPlugin {
|
|
|
643
646
|
return Promise.all(uploadPromises).then(getScreenshotUploadResult)
|
|
644
647
|
}
|
|
645
648
|
|
|
649
|
+
/**
|
|
650
|
+
* Cancels screenshot work that must not outlive an errored after:spec finalization boundary.
|
|
651
|
+
*
|
|
652
|
+
* @param {Error} error - Error that triggered finalization
|
|
653
|
+
* @returns {void}
|
|
654
|
+
*/
|
|
655
|
+
abortPendingScreenshotUploads (error) {
|
|
656
|
+
for (const controller of this.screenshotUploadAbortControllers) controller.abort(error)
|
|
657
|
+
this.screenshotUploadAbortControllers.clear()
|
|
658
|
+
this.screenshotUploadPromisesByTraceId.clear()
|
|
659
|
+
this.pendingScreenshotUploads = []
|
|
660
|
+
}
|
|
661
|
+
|
|
646
662
|
/**
|
|
647
663
|
* Returns the current time in the same coordinate system used by span
|
|
648
664
|
* start/finish. Captured at session span creation so it shares the same
|
|
@@ -663,17 +679,6 @@ class CypressPlugin {
|
|
|
663
679
|
return this.repositoryRoot || this.rootDir || process.cwd()
|
|
664
680
|
}
|
|
665
681
|
|
|
666
|
-
/**
|
|
667
|
-
* Returns whether the backend supplied skipped-test coverage data.
|
|
668
|
-
*
|
|
669
|
-
* @returns {boolean}
|
|
670
|
-
*/
|
|
671
|
-
hasSkippableTestsCoverage () {
|
|
672
|
-
return !!(this.skippableTestsCoverage &&
|
|
673
|
-
typeof this.skippableTestsCoverage === 'object' &&
|
|
674
|
-
Object.keys(this.skippableTestsCoverage).length > 0)
|
|
675
|
-
}
|
|
676
|
-
|
|
677
682
|
/**
|
|
678
683
|
* Returns whether skipped test coverage should be backfilled into the session coverage map.
|
|
679
684
|
*
|
|
@@ -683,7 +688,7 @@ class CypressPlugin {
|
|
|
683
688
|
return this.isItrEnabled &&
|
|
684
689
|
this.isCoverageReportUploadEnabled &&
|
|
685
690
|
this.isTestsSkipped &&
|
|
686
|
-
this.
|
|
691
|
+
this.skippableTestsCoverage !== undefined
|
|
687
692
|
}
|
|
688
693
|
|
|
689
694
|
/**
|
|
@@ -800,6 +805,17 @@ class CypressPlugin {
|
|
|
800
805
|
// Depending on the received configuration, the Cypress configuration can be modified:
|
|
801
806
|
// for example, to enable retries for failed tests.
|
|
802
807
|
init (tracer, cypressConfig) {
|
|
808
|
+
if (this.cypressConfig === cypressConfig && this.hasOriginalCypressRetries) {
|
|
809
|
+
cypressConfig.retries = this.originalCypressRetries !== null &&
|
|
810
|
+
typeof this.originalCypressRetries === 'object'
|
|
811
|
+
? { ...this.originalCypressRetries }
|
|
812
|
+
: this.originalCypressRetries
|
|
813
|
+
} else {
|
|
814
|
+
this.originalCypressRetries = cypressConfig.retries !== null && typeof cypressConfig.retries === 'object'
|
|
815
|
+
? { ...cypressConfig.retries }
|
|
816
|
+
: cypressConfig.retries
|
|
817
|
+
this.hasOriginalCypressRetries = true
|
|
818
|
+
}
|
|
803
819
|
this.resetRunState()
|
|
804
820
|
this._isInit = true
|
|
805
821
|
this.tracer = tracer
|
|
@@ -1063,9 +1079,13 @@ class CypressPlugin {
|
|
|
1063
1079
|
}
|
|
1064
1080
|
|
|
1065
1081
|
async beforeRun (details) {
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1082
|
+
if (this._isInit) {
|
|
1083
|
+
// The user may not have returned the promise from the init function.
|
|
1084
|
+
await this.libraryConfigurationPromise
|
|
1085
|
+
} else {
|
|
1086
|
+
// Cypress open reuses the same plugin process for every interactive run.
|
|
1087
|
+
await this.init(this.tracer, this.cypressConfig)
|
|
1088
|
+
}
|
|
1069
1089
|
|
|
1070
1090
|
this.command = getCypressCommand(details)
|
|
1071
1091
|
this.frameworkVersion = getCypressVersion(details)
|
|
@@ -1131,7 +1151,7 @@ class CypressPlugin {
|
|
|
1131
1151
|
} else {
|
|
1132
1152
|
const { skippableTests, correlationId, skippableTestsCoverage } = skippableTestsResponse
|
|
1133
1153
|
this.testsToSkip = skippableTests || []
|
|
1134
|
-
this.skippableTestsCoverage = skippableTestsCoverage
|
|
1154
|
+
this.skippableTestsCoverage = skippableTestsCoverage
|
|
1135
1155
|
this.itrCorrelationId = correlationId
|
|
1136
1156
|
incrementCountMetric(TELEMETRY_ITR_SKIPPED, { testLevel: 'test' }, this.testsToSkip.length)
|
|
1137
1157
|
}
|
|
@@ -1252,18 +1272,29 @@ class CypressPlugin {
|
|
|
1252
1272
|
return details
|
|
1253
1273
|
}
|
|
1254
1274
|
|
|
1255
|
-
afterRun (suiteStats) {
|
|
1275
|
+
afterRun (suiteStats, error, shouldFailFinishedSuites = true) {
|
|
1256
1276
|
if (!this._isInit) {
|
|
1257
1277
|
log.warn('Attemping to call afterRun without initializating the plugin first')
|
|
1258
1278
|
return
|
|
1259
1279
|
}
|
|
1260
1280
|
if (this.testSessionSpan && this.testModuleSpan) {
|
|
1261
|
-
const testStatus = getSessionStatus(suiteStats)
|
|
1281
|
+
const testStatus = error ? 'fail' : getSessionStatus(suiteStats)
|
|
1262
1282
|
const hasBackfilledCoverage = this.applySkippedCoverageToTestSessionCoverage()
|
|
1263
1283
|
const testCodeCoverageLinesTotal = this.getTestCodeCoverageLinesTotal(hasBackfilledCoverage)
|
|
1264
1284
|
|
|
1265
1285
|
this.testModuleSpan.setTag(TEST_STATUS, testStatus)
|
|
1266
1286
|
this.testSessionSpan.setTag(TEST_STATUS, testStatus)
|
|
1287
|
+
for (const span of this.finishedTestSuiteSpans) {
|
|
1288
|
+
if (error && shouldFailFinishedSuites) {
|
|
1289
|
+
span.setTag(TEST_STATUS, 'fail')
|
|
1290
|
+
span.setTag('error', error)
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
this.finishedTestSuiteSpans = []
|
|
1294
|
+
if (error) {
|
|
1295
|
+
this.testModuleSpan.setTag('error', error)
|
|
1296
|
+
this.testSessionSpan.setTag('error', error)
|
|
1297
|
+
}
|
|
1267
1298
|
|
|
1268
1299
|
addIntelligentTestRunnerSpanTags(
|
|
1269
1300
|
this.testSessionSpan,
|
|
@@ -1301,6 +1332,7 @@ class CypressPlugin {
|
|
|
1301
1332
|
})
|
|
1302
1333
|
|
|
1303
1334
|
finishAllTraceSpans(this.testSessionSpan)
|
|
1335
|
+
this.tracer._tracer._exporter?.exportDeferredTestSuiteSpans?.()
|
|
1304
1336
|
}
|
|
1305
1337
|
|
|
1306
1338
|
return new Promise(resolve => {
|
|
@@ -1358,7 +1390,7 @@ class CypressPlugin {
|
|
|
1358
1390
|
}
|
|
1359
1391
|
}
|
|
1360
1392
|
|
|
1361
|
-
afterSpec (spec, results) {
|
|
1393
|
+
afterSpec (spec, results, error) {
|
|
1362
1394
|
const { tests, stats, screenshots } = results || {}
|
|
1363
1395
|
const cypressTests = tests || []
|
|
1364
1396
|
const specScreenshots = screenshots || []
|
|
@@ -1572,13 +1604,16 @@ class CypressPlugin {
|
|
|
1572
1604
|
const screenshotUploadResultPromise = failedTestTraceId
|
|
1573
1605
|
? this.getScreenshotUploadResultPromise(failedTestTraceId)
|
|
1574
1606
|
: undefined
|
|
1575
|
-
if (screenshotUploadResultPromise) {
|
|
1607
|
+
if (screenshotUploadResultPromise && !error) {
|
|
1576
1608
|
testSpanFinishPromises.push(screenshotUploadResultPromise.then((uploadResult) => {
|
|
1577
1609
|
setScreenshotUploadTags(finishedTest.testSpan, uploadResult)
|
|
1578
1610
|
this.screenshotUploadPromisesByTraceId.delete(failedTestTraceId)
|
|
1579
1611
|
finishedTest.testSpan.finish(finishedTest.finishTime)
|
|
1580
1612
|
}))
|
|
1581
1613
|
} else {
|
|
1614
|
+
if (screenshotUploadResultPromise) {
|
|
1615
|
+
this.screenshotUploadPromisesByTraceId.delete(failedTestTraceId)
|
|
1616
|
+
}
|
|
1582
1617
|
finishedTest.testSpan.finish(finishedTest.finishTime)
|
|
1583
1618
|
}
|
|
1584
1619
|
}
|
|
@@ -1586,13 +1621,18 @@ class CypressPlugin {
|
|
|
1586
1621
|
|
|
1587
1622
|
const finishSuite = () => {
|
|
1588
1623
|
if (this.testSuiteSpan) {
|
|
1589
|
-
const status = getSuiteStatus(stats)
|
|
1624
|
+
const status = error ? 'fail' : getSuiteStatus(stats)
|
|
1590
1625
|
this.testSuiteSpan.setTag(TEST_STATUS, status)
|
|
1591
1626
|
|
|
1592
|
-
if (latestError) {
|
|
1593
|
-
this.testSuiteSpan.setTag('error', latestError)
|
|
1627
|
+
if (error || latestError) {
|
|
1628
|
+
this.testSuiteSpan.setTag('error', error || latestError)
|
|
1594
1629
|
}
|
|
1630
|
+
const canRunAfterRun = this.cypressConfig.isTextTerminal ||
|
|
1631
|
+
this.cypressConfig.experimentalInteractiveRunEvents
|
|
1632
|
+
const exporter = this.tracer._tracer._exporter
|
|
1633
|
+
if (canRunAfterRun && exporter?.deferTestSuiteSpan) exporter.deferTestSuiteSpan(this.testSuiteSpan)
|
|
1595
1634
|
this.testSuiteSpan.finish()
|
|
1635
|
+
if (canRunAfterRun) this.finishedTestSuiteSpans.push(this.testSuiteSpan)
|
|
1596
1636
|
this.testSuiteSpan = null
|
|
1597
1637
|
this.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'suite')
|
|
1598
1638
|
}
|
|
@@ -1608,16 +1648,23 @@ class CypressPlugin {
|
|
|
1608
1648
|
|
|
1609
1649
|
finishSuite()
|
|
1610
1650
|
|
|
1651
|
+
if (error) {
|
|
1652
|
+
this.abortPendingScreenshotUploads(error)
|
|
1653
|
+
return this.afterRun(undefined, error, false)
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1611
1656
|
const screenshotUploadsPromise = waitForScreenshotUploads()
|
|
1657
|
+
let afterSpecPromise = screenshotUploadsPromise
|
|
1612
1658
|
if (testSpanFinishPromises.length > 0) {
|
|
1613
1659
|
const testSpansPromise = Promise.all(testSpanFinishPromises).then(() => null)
|
|
1614
1660
|
if (screenshotUploadsPromise) {
|
|
1615
|
-
|
|
1661
|
+
afterSpecPromise = Promise.all([testSpansPromise, screenshotUploadsPromise]).then(() => null)
|
|
1662
|
+
} else {
|
|
1663
|
+
afterSpecPromise = testSpansPromise
|
|
1616
1664
|
}
|
|
1617
|
-
return testSpansPromise
|
|
1618
1665
|
}
|
|
1619
1666
|
|
|
1620
|
-
return
|
|
1667
|
+
return afterSpecPromise
|
|
1621
1668
|
}
|
|
1622
1669
|
|
|
1623
1670
|
/**
|
|
@@ -1637,6 +1684,7 @@ class CypressPlugin {
|
|
|
1637
1684
|
}
|
|
1638
1685
|
|
|
1639
1686
|
const uploadPromises = []
|
|
1687
|
+
const abortController = new AbortController()
|
|
1640
1688
|
|
|
1641
1689
|
for (const screenshot of screenshots) {
|
|
1642
1690
|
const filePath = getScreenshotFilePath(screenshot)
|
|
@@ -1655,6 +1703,7 @@ class CypressPlugin {
|
|
|
1655
1703
|
traceId,
|
|
1656
1704
|
idempotencyKey,
|
|
1657
1705
|
capturedAtMs,
|
|
1706
|
+
signal: abortController.signal,
|
|
1658
1707
|
}, (err) => {
|
|
1659
1708
|
resolve(err ? SCREENSHOT_UPLOAD_RESULT_ERROR : SCREENSHOT_UPLOAD_RESULT_UPLOADED)
|
|
1660
1709
|
})
|
|
@@ -1662,7 +1711,10 @@ class CypressPlugin {
|
|
|
1662
1711
|
}
|
|
1663
1712
|
|
|
1664
1713
|
if (uploadPromises.length > 0) {
|
|
1665
|
-
|
|
1714
|
+
this.screenshotUploadAbortControllers.add(abortController)
|
|
1715
|
+
const uploadPromise = Promise.all(uploadPromises).then(getScreenshotUploadResult).finally(() => {
|
|
1716
|
+
this.screenshotUploadAbortControllers.delete(abortController)
|
|
1717
|
+
})
|
|
1666
1718
|
this.addScreenshotUploadPromise(traceId, uploadPromise)
|
|
1667
1719
|
return uploadPromise
|
|
1668
1720
|
}
|