dd-trace 6.9.0 → 6.11.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/ci/vitest-no-worker-init-setup.mjs +112 -10
- package/index.d.ts +150 -3
- package/initialize.mjs +28 -17
- package/openfeature.js +2 -2
- package/package.json +14 -10
- 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/confluentinc-kafka-javascript.js +111 -6
- 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 +634 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +10 -8
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +2 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +81 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +83 -1
- package/packages/datadog-instrumentations/src/jest.js +26 -18
- package/packages/datadog-instrumentations/src/mariadb.js +278 -21
- package/packages/datadog-instrumentations/src/mocha/main.js +540 -56
- package/packages/datadog-instrumentations/src/mocha/utils.js +132 -25
- package/packages/datadog-instrumentations/src/mysql.js +45 -15
- package/packages/datadog-instrumentations/src/mysql2.js +119 -18
- package/packages/datadog-instrumentations/src/path-to-regexp.js +67 -1
- package/packages/datadog-instrumentations/src/pg.js +116 -92
- 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-no-worker-init.js +43 -4
- package/packages/datadog-instrumentations/src/vitest-main.js +180 -20
- package/packages/datadog-instrumentations/src/vitest-util.js +64 -5
- package/packages/datadog-instrumentations/src/vitest-worker.js +150 -25
- package/packages/datadog-instrumentations/src/webdriverio.js +31 -17
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/trace-checkpoint.js +12 -6
- 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 +4 -13
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -5
- 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 +82 -30
- 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-google-cloud-pubsub/src/producer.js +31 -17
- package/packages/datadog-plugin-jest/src/index.js +11 -4
- package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +2 -3
- package/packages/datadog-plugin-kafkajs/src/consumer.js +2 -3
- package/packages/datadog-plugin-kafkajs/src/producer.js +3 -2
- package/packages/datadog-plugin-kafkajs/src/utils.js +65 -7
- package/packages/datadog-plugin-mocha/src/index.js +606 -40
- package/packages/datadog-plugin-mysql/src/index.js +37 -0
- package/packages/datadog-plugin-nats/src/index.js +1 -1
- package/packages/datadog-plugin-next/src/index.js +1 -19
- package/packages/datadog-plugin-openai/src/tracing.js +41 -30
- package/packages/datadog-plugin-openai-agents/src/integration.js +279 -69
- package/packages/datadog-plugin-openai-agents/src/processor.js +16 -4
- package/packages/datadog-plugin-pg/src/index.js +1 -0
- package/packages/datadog-plugin-playwright/src/index.js +42 -13
- package/packages/datadog-plugin-vitest/src/index.js +14 -20
- package/packages/datadog-plugin-ws/src/util.js +2 -1
- 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/api_security/normalized-route.js +453 -0
- package/packages/dd-trace/src/appsec/handlers/http-request.js +13 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +9 -12
- package/packages/dd-trace/src/carrier.js +356 -0
- 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 +7 -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 +24 -0
- package/packages/dd-trace/src/datastreams/context.js +6 -1
- package/packages/dd-trace/src/datastreams/index.js +2 -0
- package/packages/dd-trace/src/datastreams/manager.js +14 -3
- package/packages/dd-trace/src/datastreams/pathway.js +23 -22
- package/packages/dd-trace/src/datastreams/processor.js +16 -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/constants/tags.js +5 -0
- 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/index.js +34 -2
- 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/llmobs/span_processor.js +16 -0
- package/packages/dd-trace/src/llmobs/tagger.js +96 -0
- package/packages/dd-trace/src/llmobs/util.js +78 -0
- 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/propagation/text_map.js +126 -120
- package/packages/dd-trace/src/opentracing/propagation/text_map_dsm.js +4 -8
- 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 +5 -3
- package/packages/dd-trace/src/plugins/ci_plugin.js +6 -1
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/{datadog-plugin-jest/src/util.js → dd-trace/src/plugins/util/jest.js} +5 -65
- package/packages/dd-trace/src/plugins/util/test.js +93 -14
- package/packages/dd-trace/src/plugins/util/web.js +56 -2
- 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
|
@@ -12,13 +12,16 @@ const { getTestManagementTests: getTestManagementTestsRequest } =
|
|
|
12
12
|
require('../test-management/get-test-management-tests')
|
|
13
13
|
const { writeSettingsToCache } = require('../test-optimization-cache')
|
|
14
14
|
const { CACHE_MISS, TestOptimizationHttpCache } = require('../test-optimization-http-cache')
|
|
15
|
+
const { incrementCountMetric, TELEMETRY_EVENTS_ENQUEUED_FOR_SERIALIZATION } = require('../telemetry')
|
|
15
16
|
const { uploadCoverageReport: uploadCoverageReportRequest } = require('../requests/upload-coverage-report')
|
|
16
17
|
const { uploadTestScreenshot: uploadTestScreenshotRequest } = require('../requests/upload-test-screenshot')
|
|
17
18
|
const { parsers } = require('../../config/parsers')
|
|
18
19
|
const log = require('../../log')
|
|
20
|
+
const spanFormat = require('../../span_format')
|
|
19
21
|
const { getSegment } = require('../../util')
|
|
20
22
|
const BufferingExporter = require('../../exporters/common/buffering-exporter')
|
|
21
23
|
const { GIT_REPOSITORY_URL, GIT_COMMIT_SHA } = require('../../plugins/util/tags')
|
|
24
|
+
const { TEST_STATUS } = require('../../plugins/util/test')
|
|
22
25
|
const { sendGitMetadata: sendGitMetadataRequest } = require('./git/git_metadata')
|
|
23
26
|
|
|
24
27
|
const hostname = getHostname()
|
|
@@ -57,6 +60,17 @@ function getIsTestSessionTrace (trace) {
|
|
|
57
60
|
const GIT_UPLOAD_TIMEOUT = 60_000 // 60 seconds
|
|
58
61
|
const CAN_USE_CI_VIS_PROTOCOL_TIMEOUT = GIT_UPLOAD_TIMEOUT
|
|
59
62
|
const MAX_COVERAGE_REPORT_FLAGS = 32
|
|
63
|
+
const FINAL_FLUSH_TIMEOUT = 10_000
|
|
64
|
+
const FINAL_FLUSH_FALLBACK_DELAY = 100
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @returns {Error}
|
|
68
|
+
*/
|
|
69
|
+
function createFinalFlushTimeoutError () {
|
|
70
|
+
const error = new Error('Timed out waiting for Test Optimization to flush')
|
|
71
|
+
error.code = 'ERR_DD_TEST_OPTIMIZATION_FLUSH_TIMEOUT'
|
|
72
|
+
return error
|
|
73
|
+
}
|
|
60
74
|
|
|
61
75
|
function appendLogTag (tags, key, value) {
|
|
62
76
|
if (value !== undefined) {
|
|
@@ -87,6 +101,12 @@ function getLogTags (logMessage, { env, version }, gitRepositoryUrl, gitCommitSh
|
|
|
87
101
|
}
|
|
88
102
|
|
|
89
103
|
class CiVisibilityExporter extends BufferingExporter {
|
|
104
|
+
#finalFlush
|
|
105
|
+
#deferredTestSessionTraces = []
|
|
106
|
+
#pendingScreenshotUploads = new Set()
|
|
107
|
+
#screenshotFlushWaiters = new Set()
|
|
108
|
+
#deferredTestSuiteSpans = new Map()
|
|
109
|
+
|
|
90
110
|
constructor (config, options = {}) {
|
|
91
111
|
super(config)
|
|
92
112
|
this._timer = undefined
|
|
@@ -137,18 +157,7 @@ class CiVisibilityExporter extends BufferingExporter {
|
|
|
137
157
|
}
|
|
138
158
|
})
|
|
139
159
|
|
|
140
|
-
|
|
141
|
-
if (this._writer) {
|
|
142
|
-
this._writer.flush()
|
|
143
|
-
}
|
|
144
|
-
if (this._coverageWriter) {
|
|
145
|
-
this._coverageWriter.flush()
|
|
146
|
-
}
|
|
147
|
-
if (this._logsWriter) {
|
|
148
|
-
this._logsWriter.flush()
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
globalThis[Symbol.for('dd-trace')].beforeExitHandlers.add(flush.bind(this))
|
|
160
|
+
globalThis[Symbol.for('dd-trace')].beforeExitHandlers.add(() => this.flush(() => {}))
|
|
152
161
|
}
|
|
153
162
|
|
|
154
163
|
shouldRequestSkippableSuites () {
|
|
@@ -399,18 +408,189 @@ class CiVisibilityExporter extends BufferingExporter {
|
|
|
399
408
|
}
|
|
400
409
|
|
|
401
410
|
export (trace) {
|
|
411
|
+
this.#resetFinalFlush()
|
|
412
|
+
|
|
413
|
+
if (this.#deferredTestSuiteSpans.size === 0) {
|
|
414
|
+
this.#exportTrace(trace)
|
|
415
|
+
return
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
let hasDeferredTestSuiteSpan = false
|
|
419
|
+
let immediateTrace
|
|
420
|
+
for (let index = 0; index < trace.length; index++) {
|
|
421
|
+
const formattedSpan = trace[index]
|
|
422
|
+
const spanId = formattedSpan.span_id?.toString()
|
|
423
|
+
const deferredTestSuiteSpan = this.#deferredTestSuiteSpans.get(spanId)
|
|
424
|
+
if (deferredTestSuiteSpan) {
|
|
425
|
+
if (!hasDeferredTestSuiteSpan && index > 0) immediateTrace = trace.slice(0, index)
|
|
426
|
+
hasDeferredTestSuiteSpan = true
|
|
427
|
+
if (deferredTestSuiteSpan.emitted || (this._isInitialized && !this.canReportSessionTraces())) {
|
|
428
|
+
this.#deferredTestSuiteSpans.delete(spanId)
|
|
429
|
+
} else {
|
|
430
|
+
deferredTestSuiteSpan.formattedSpan = formattedSpan
|
|
431
|
+
}
|
|
432
|
+
} else if (hasDeferredTestSuiteSpan) {
|
|
433
|
+
immediateTrace ??= []
|
|
434
|
+
immediateTrace.push(formattedSpan)
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
if (!hasDeferredTestSuiteSpan) this.#exportTrace(trace)
|
|
439
|
+
else if (immediateTrace) this.#exportTrace(immediateTrace)
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Exports spans that are not retained for late updates to session, module, or suite events.
|
|
444
|
+
*
|
|
445
|
+
* @param {Array<object>} trace
|
|
446
|
+
* @returns {void}
|
|
447
|
+
*/
|
|
448
|
+
#exportTrace (trace) {
|
|
402
449
|
// Until it's initialized, we just store the traces as is
|
|
403
450
|
if (!this._isInitialized) {
|
|
404
451
|
this._traceBuffer.push(trace)
|
|
405
452
|
return
|
|
406
453
|
}
|
|
407
|
-
|
|
454
|
+
const isTestSessionTrace = getIsTestSessionTrace(trace)
|
|
455
|
+
if (!this.canReportSessionTraces() && isTestSessionTrace) {
|
|
408
456
|
return
|
|
409
457
|
}
|
|
410
|
-
this._export(trace)
|
|
458
|
+
if (this._export(trace, undefined, undefined, isTestSessionTrace) === false && isTestSessionTrace) {
|
|
459
|
+
this.#deferredTestSessionTraces.push(trace)
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Retains a completed suite until finalization so a later framework error can still update it.
|
|
465
|
+
*
|
|
466
|
+
* @param {import('../../opentracing/span')} testSuiteSpan
|
|
467
|
+
* @returns {void}
|
|
468
|
+
*/
|
|
469
|
+
deferTestSuiteSpan (testSuiteSpan) {
|
|
470
|
+
this.#resetFinalFlush()
|
|
471
|
+
this.#deferredTestSuiteSpans.set(testSuiteSpan.context()._spanId.toString(), {
|
|
472
|
+
span: testSuiteSpan,
|
|
473
|
+
formattedSpan: undefined,
|
|
474
|
+
emitted: false,
|
|
475
|
+
})
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Retains formatted test suite events received from test framework workers until finalization.
|
|
480
|
+
*
|
|
481
|
+
* @param {Array<object>} trace
|
|
482
|
+
* @returns {void}
|
|
483
|
+
*/
|
|
484
|
+
exportTraceWithDeferredTestSuite (trace) {
|
|
485
|
+
for (const formattedSpan of trace) {
|
|
486
|
+
if (formattedSpan.type !== 'test_suite_end') continue
|
|
487
|
+
|
|
488
|
+
this.#deferredTestSuiteSpans.set(formattedSpan.span_id.toString(), {
|
|
489
|
+
span: undefined,
|
|
490
|
+
formattedSpan,
|
|
491
|
+
emitted: false,
|
|
492
|
+
})
|
|
493
|
+
}
|
|
494
|
+
this.export(trace)
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* Applies a late framework error to every completed suite retained by the current finalization boundary.
|
|
499
|
+
*
|
|
500
|
+
* @param {Error} error
|
|
501
|
+
* @returns {void}
|
|
502
|
+
*/
|
|
503
|
+
setDeferredTestSuiteError (error) {
|
|
504
|
+
for (const { span, formattedSpan, emitted } of this.#deferredTestSuiteSpans.values()) {
|
|
505
|
+
if (emitted) continue
|
|
506
|
+
if (span) {
|
|
507
|
+
span.setTag(TEST_STATUS, 'fail')
|
|
508
|
+
span.setTag('error', error)
|
|
509
|
+
} else if (formattedSpan) {
|
|
510
|
+
formattedSpan.meta[TEST_STATUS] = 'fail'
|
|
511
|
+
spanFormat.addError(formattedSpan, error)
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Appends a completed suite while preserving the ordinary writer call shape outside finalization.
|
|
518
|
+
*
|
|
519
|
+
* @param {object} formattedSpan
|
|
520
|
+
* @param {{ deadline?: number }} [options]
|
|
521
|
+
* @returns {boolean|undefined}
|
|
522
|
+
*/
|
|
523
|
+
#appendDeferredTestSuiteSpan (formattedSpan, options) {
|
|
524
|
+
const appended = options === undefined
|
|
525
|
+
? this._writer.append([formattedSpan])
|
|
526
|
+
: this._writer.append([formattedSpan], options)
|
|
527
|
+
|
|
528
|
+
if (appended !== false && this._config.isCiVisibility) {
|
|
529
|
+
incrementCountMetric(TELEMETRY_EVENTS_ENQUEUED_FOR_SERIALIZATION)
|
|
530
|
+
}
|
|
531
|
+
return appended
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Appends completed suites before their module and session parents are exported.
|
|
536
|
+
*
|
|
537
|
+
* @param {{ deadline?: number }} [options]
|
|
538
|
+
* @returns {void}
|
|
539
|
+
*/
|
|
540
|
+
exportDeferredTestSuiteSpans (options) {
|
|
541
|
+
if (!this._writer || !this.canReportSessionTraces()) return
|
|
542
|
+
|
|
543
|
+
for (const [spanId, deferredTestSuiteSpan] of this.#deferredTestSuiteSpans) {
|
|
544
|
+
const { span, formattedSpan, emitted } = deferredTestSuiteSpan
|
|
545
|
+
if (emitted) continue
|
|
546
|
+
const updatedSpan = span && spanFormat(span)
|
|
547
|
+
if (span && formattedSpan) {
|
|
548
|
+
formattedSpan.error = updatedSpan.error
|
|
549
|
+
Object.assign(formattedSpan.meta, updatedSpan.meta)
|
|
550
|
+
Object.assign(formattedSpan.metrics, updatedSpan.metrics)
|
|
551
|
+
if (this.#appendDeferredTestSuiteSpan(formattedSpan, options) === false) continue
|
|
552
|
+
this.#deferredTestSuiteSpans.delete(spanId)
|
|
553
|
+
} else if (formattedSpan) {
|
|
554
|
+
if (this.#appendDeferredTestSuiteSpan(formattedSpan, options) === false) continue
|
|
555
|
+
this.#deferredTestSuiteSpans.delete(spanId)
|
|
556
|
+
} else {
|
|
557
|
+
if (this.#appendDeferredTestSuiteSpan(updatedSpan, options) === false) continue
|
|
558
|
+
deferredTestSuiteSpan.emitted = true
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* Discards completed suites that the selected transport cannot deliver.
|
|
565
|
+
*
|
|
566
|
+
* @returns {void}
|
|
567
|
+
*/
|
|
568
|
+
resetDeferredTestSuiteSpans () {
|
|
569
|
+
this.#deferredTestSuiteSpans.clear()
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* Retries session, module, and suite traces rejected by writer backpressure within the final deadline.
|
|
574
|
+
*
|
|
575
|
+
* @param {{ deadline?: number }} options final-flush options
|
|
576
|
+
* @returns {void}
|
|
577
|
+
*/
|
|
578
|
+
#exportDeferredTestSessionTraces (options) {
|
|
579
|
+
if (!this._writer || !this.canReportSessionTraces()) return
|
|
580
|
+
|
|
581
|
+
let retainedCount = 0
|
|
582
|
+
|
|
583
|
+
for (const trace of this.#deferredTestSessionTraces) {
|
|
584
|
+
if (this._writer.append(trace, options) === false) {
|
|
585
|
+
this.#deferredTestSessionTraces[retainedCount++] = trace
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
this.#deferredTestSessionTraces.length = retainedCount
|
|
411
589
|
}
|
|
412
590
|
|
|
413
591
|
exportCoverage (formattedCoverage) {
|
|
592
|
+
this.#resetFinalFlush()
|
|
593
|
+
|
|
414
594
|
// Until it's initialized, we just store the coverages as is
|
|
415
595
|
if (!this._isInitialized) {
|
|
416
596
|
this._coverageBuffer.push(formattedCoverage)
|
|
@@ -455,6 +635,7 @@ class CiVisibilityExporter extends BufferingExporter {
|
|
|
455
635
|
return
|
|
456
636
|
}
|
|
457
637
|
|
|
638
|
+
this.#resetFinalFlush()
|
|
458
639
|
this._export(
|
|
459
640
|
this.formatLogMessage(testEnvironmentMetadata, logMessage),
|
|
460
641
|
this._logsWriter,
|
|
@@ -462,32 +643,140 @@ class CiVisibilityExporter extends BufferingExporter {
|
|
|
462
643
|
)
|
|
463
644
|
}
|
|
464
645
|
|
|
465
|
-
flush (done
|
|
466
|
-
|
|
467
|
-
|
|
646
|
+
flush (done) {
|
|
647
|
+
const isFinalFlush = typeof done === 'function'
|
|
648
|
+
const onDone = done || (() => {})
|
|
649
|
+
let finalFlush
|
|
650
|
+
|
|
651
|
+
if (isFinalFlush && this.#finalFlush) {
|
|
652
|
+
if (this.#finalFlush.completed) onDone(this.#finalFlush.error)
|
|
653
|
+
else this.#finalFlush.callbacks.push(onDone)
|
|
654
|
+
return
|
|
468
655
|
}
|
|
469
656
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
this.
|
|
473
|
-
this.
|
|
474
|
-
|
|
475
|
-
|
|
657
|
+
if (isFinalFlush && !this._isInitialized &&
|
|
658
|
+
this._traceBuffer.length === 0 && this._coverageBuffer.length === 0 &&
|
|
659
|
+
this.#deferredTestSuiteSpans.size === 0 && this.#deferredTestSessionTraces.length === 0 &&
|
|
660
|
+
this.#pendingScreenshotUploads.size === 0) {
|
|
661
|
+
onDone()
|
|
662
|
+
return
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
if (isFinalFlush) {
|
|
666
|
+
finalFlush = {
|
|
667
|
+
callbacks: [onDone],
|
|
668
|
+
completed: false,
|
|
669
|
+
error: undefined,
|
|
670
|
+
}
|
|
671
|
+
this.#finalFlush = finalFlush
|
|
672
|
+
}
|
|
476
673
|
|
|
477
|
-
|
|
674
|
+
const deadline = isFinalFlush ? Date.now() + FINAL_FLUSH_TIMEOUT : undefined
|
|
675
|
+
let hasCompleted = false
|
|
676
|
+
let initializationTimeoutId
|
|
677
|
+
|
|
678
|
+
const fallbackTimeoutId = isFinalFlush
|
|
679
|
+
? setTimeout(() => {
|
|
680
|
+
complete(createFinalFlushTimeoutError())
|
|
681
|
+
}, FINAL_FLUSH_TIMEOUT + FINAL_FLUSH_FALLBACK_DELAY)
|
|
682
|
+
: undefined
|
|
683
|
+
|
|
684
|
+
const complete = (error) => {
|
|
685
|
+
if (hasCompleted) return
|
|
686
|
+
hasCompleted = true
|
|
687
|
+
clearTimeout(fallbackTimeoutId)
|
|
688
|
+
clearTimeout(initializationTimeoutId)
|
|
689
|
+
this.#screenshotFlushWaiters.delete(flushWriters)
|
|
690
|
+
if (error) log.error('Error flushing Test Optimization data', error)
|
|
691
|
+
if (!isFinalFlush) {
|
|
692
|
+
onDone(error)
|
|
693
|
+
return
|
|
694
|
+
}
|
|
478
695
|
|
|
479
|
-
|
|
480
|
-
|
|
696
|
+
finalFlush.completed = true
|
|
697
|
+
finalFlush.error = error
|
|
698
|
+
const callbacks = finalFlush.callbacks
|
|
699
|
+
finalFlush.callbacks = []
|
|
700
|
+
for (const callback of callbacks) {
|
|
701
|
+
try {
|
|
702
|
+
callback(error)
|
|
703
|
+
} catch (callbackError) {
|
|
704
|
+
log.error('Error completing Test Optimization flush callback', callbackError)
|
|
705
|
+
}
|
|
706
|
+
}
|
|
481
707
|
}
|
|
482
708
|
|
|
483
|
-
const
|
|
484
|
-
|
|
709
|
+
const flushWriters = () => {
|
|
710
|
+
if (isFinalFlush && this.#pendingScreenshotUploads.size !== 0) {
|
|
711
|
+
this.#screenshotFlushWaiters.add(flushWriters)
|
|
712
|
+
return
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
const options = deadline === undefined ? undefined : { deadline }
|
|
716
|
+
if (isFinalFlush) {
|
|
717
|
+
this.exportDeferredTestSuiteSpans(options)
|
|
718
|
+
this.#exportDeferredTestSessionTraces(options)
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
const writers = [
|
|
722
|
+
this._writer,
|
|
723
|
+
this._coverageWriter,
|
|
724
|
+
this._logsWriter,
|
|
725
|
+
].filter(Boolean)
|
|
726
|
+
|
|
727
|
+
let remaining = writers.length
|
|
728
|
+
let flushError
|
|
729
|
+
|
|
485
730
|
if (remaining === 0) {
|
|
486
|
-
|
|
731
|
+
complete()
|
|
732
|
+
return
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
const onFlushComplete = (error) => {
|
|
736
|
+
flushError ||= error
|
|
737
|
+
remaining -= 1
|
|
738
|
+
if (remaining === 0) complete(flushError)
|
|
487
739
|
}
|
|
740
|
+
|
|
741
|
+
for (const writer of writers) writer.flush(onFlushComplete, options)
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
if (isFinalFlush && this._initializationRequest) {
|
|
745
|
+
const initializationRequest = this._initializationRequest
|
|
746
|
+
const { controller, options } = initializationRequest
|
|
747
|
+
initializationRequest.finalFlush = finalFlush
|
|
748
|
+
options.deadline = deadline
|
|
749
|
+
initializationTimeoutId = setTimeout(() => {
|
|
750
|
+
const error = createFinalFlushTimeoutError()
|
|
751
|
+
if (initializationRequest.finalFlush === finalFlush) controller.abort(error)
|
|
752
|
+
complete(error)
|
|
753
|
+
}, Math.max(0, deadline - Date.now()))
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
if (!isFinalFlush) {
|
|
757
|
+
if (this._isInitialized) flushWriters()
|
|
758
|
+
else complete()
|
|
759
|
+
return
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
if (this._isInitialized) {
|
|
763
|
+
flushWriters()
|
|
764
|
+
return
|
|
488
765
|
}
|
|
489
766
|
|
|
490
|
-
|
|
767
|
+
this._canUseCiVisProtocolPromise.then(() => {
|
|
768
|
+
clearTimeout(initializationTimeoutId)
|
|
769
|
+
if (!hasCompleted) flushWriters()
|
|
770
|
+
})
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
/**
|
|
774
|
+
* Allows later test activity to establish a new finalization boundary.
|
|
775
|
+
*
|
|
776
|
+
* @returns {void}
|
|
777
|
+
*/
|
|
778
|
+
#resetFinalFlush () {
|
|
779
|
+
this.#finalFlush = undefined
|
|
491
780
|
}
|
|
492
781
|
|
|
493
782
|
exportUncodedCoverages () {
|
|
@@ -572,13 +861,55 @@ class CiVisibilityExporter extends BufferingExporter {
|
|
|
572
861
|
* @param {string} options.traceId - Test trace id used as the screenshot key
|
|
573
862
|
* @param {string} options.idempotencyKey - Stable per-artifact key, reused on retry
|
|
574
863
|
* @param {number} options.capturedAtMs - Capture time in epoch milliseconds
|
|
864
|
+
* @param {AbortSignal} [options.signal] - Additional signal used to cancel the upload
|
|
575
865
|
* @param {Function} callback - Callback function (err)
|
|
576
866
|
*/
|
|
577
|
-
uploadTestScreenshot ({ filePath, traceId, idempotencyKey, capturedAtMs }, callback) {
|
|
867
|
+
uploadTestScreenshot ({ filePath, traceId, idempotencyKey, capturedAtMs, signal }, callback) {
|
|
578
868
|
if (!this._testScreenshotUploadUrl) {
|
|
579
869
|
return callback(new Error('Test screenshot upload URL not configured'))
|
|
580
870
|
}
|
|
581
871
|
|
|
872
|
+
this.#resetFinalFlush()
|
|
873
|
+
const controller = new AbortController()
|
|
874
|
+
const deadline = Date.now() + FINAL_FLUSH_TIMEOUT
|
|
875
|
+
let settled = false
|
|
876
|
+
const complete = (error) => {
|
|
877
|
+
if (settled) return
|
|
878
|
+
settled = true
|
|
879
|
+
clearTimeout(timeoutId)
|
|
880
|
+
signal?.removeEventListener('abort', onAbort)
|
|
881
|
+
|
|
882
|
+
try {
|
|
883
|
+
callback(error)
|
|
884
|
+
} finally {
|
|
885
|
+
this.#pendingScreenshotUploads.delete(controller)
|
|
886
|
+
if (this.#pendingScreenshotUploads.size === 0) {
|
|
887
|
+
const waiters = [...this.#screenshotFlushWaiters]
|
|
888
|
+
this.#screenshotFlushWaiters.clear()
|
|
889
|
+
for (const waiter of waiters) queueMicrotask(waiter)
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
const onAbort = () => {
|
|
894
|
+
const error = signal.reason || Object.assign(new Error('Test screenshot upload aborted'), { code: 'ABORT_ERR' })
|
|
895
|
+
controller.abort(error)
|
|
896
|
+
complete(error)
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
this.#pendingScreenshotUploads.add(controller)
|
|
900
|
+
signal?.addEventListener('abort', onAbort, { once: true })
|
|
901
|
+
const timeoutId = setTimeout(() => {
|
|
902
|
+
const error = createFinalFlushTimeoutError()
|
|
903
|
+
controller.abort(error)
|
|
904
|
+
complete(error)
|
|
905
|
+
}, FINAL_FLUSH_TIMEOUT)
|
|
906
|
+
timeoutId.unref?.()
|
|
907
|
+
|
|
908
|
+
if (signal?.aborted) {
|
|
909
|
+
onAbort()
|
|
910
|
+
return
|
|
911
|
+
}
|
|
912
|
+
|
|
582
913
|
uploadTestScreenshotRequest({
|
|
583
914
|
filePath,
|
|
584
915
|
traceId,
|
|
@@ -587,7 +918,9 @@ class CiVisibilityExporter extends BufferingExporter {
|
|
|
587
918
|
url: this._testScreenshotUploadUrl,
|
|
588
919
|
isEvpProxy: !!this._isUsingEvpProxy,
|
|
589
920
|
evpProxyPrefix: this.evpProxyPrefix,
|
|
590
|
-
|
|
921
|
+
deadline,
|
|
922
|
+
signal: controller.signal,
|
|
923
|
+
}, complete)
|
|
591
924
|
}
|
|
592
925
|
}
|
|
593
926
|
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { Readable } = require('node:stream')
|
|
4
|
+
|
|
5
|
+
const commonRequest = require('../../exporters/common/request')
|
|
6
|
+
const {
|
|
7
|
+
RATE_LIMIT_MAX_WAIT_MS,
|
|
8
|
+
getMaxAttempts,
|
|
9
|
+
getRetryDelay,
|
|
10
|
+
isRetriableNetworkError,
|
|
11
|
+
} = require('../../exporters/common/retry')
|
|
12
|
+
const { getRateLimitResetDelay } = require('../requests/rate-limit')
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {AbortSignal} signal
|
|
16
|
+
* @returns {Error}
|
|
17
|
+
*/
|
|
18
|
+
function getAbortError (signal) {
|
|
19
|
+
return signal.reason || Object.assign(new Error('Request aborted'), { code: 'ABORT_ERR' })
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Buffers a stream once so a Test Optimization exporter request can safely retry it.
|
|
24
|
+
*
|
|
25
|
+
* @param {Readable} data
|
|
26
|
+
* @param {AbortSignal} [signal]
|
|
27
|
+
* @param {(error: Error|null, data?: Buffer) => void} callback
|
|
28
|
+
* @returns {void}
|
|
29
|
+
*/
|
|
30
|
+
function bufferReadable (data, signal, callback) {
|
|
31
|
+
const chunks = []
|
|
32
|
+
let settled = false
|
|
33
|
+
|
|
34
|
+
const cleanup = (keepErrorListener = false) => {
|
|
35
|
+
data.removeListener('data', onData)
|
|
36
|
+
data.removeListener('end', onEnd)
|
|
37
|
+
if (!keepErrorListener) data.removeListener('error', onError)
|
|
38
|
+
signal?.removeEventListener('abort', onAbort)
|
|
39
|
+
}
|
|
40
|
+
const onData = chunk => chunks.push(chunk)
|
|
41
|
+
const onEnd = () => {
|
|
42
|
+
if (settled) return
|
|
43
|
+
settled = true
|
|
44
|
+
cleanup()
|
|
45
|
+
callback(null, Buffer.concat(chunks))
|
|
46
|
+
}
|
|
47
|
+
const onError = error => {
|
|
48
|
+
if (settled) return
|
|
49
|
+
settled = true
|
|
50
|
+
cleanup()
|
|
51
|
+
callback(error)
|
|
52
|
+
}
|
|
53
|
+
const onAbort = () => {
|
|
54
|
+
if (settled) return
|
|
55
|
+
settled = true
|
|
56
|
+
cleanup(true)
|
|
57
|
+
data.once('close', () => data.removeListener('error', onError))
|
|
58
|
+
data.destroy()
|
|
59
|
+
callback(getAbortError(signal))
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
data.on('data', onData)
|
|
63
|
+
data.once('end', onEnd)
|
|
64
|
+
data.once('error', onError)
|
|
65
|
+
signal?.addEventListener('abort', onAbort, { once: true })
|
|
66
|
+
if (signal?.aborted) onAbort()
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Sends Test Optimization exporter data through the common single-attempt transport while
|
|
71
|
+
* keeping retry and finalization policy scoped to Test Optimization.
|
|
72
|
+
*
|
|
73
|
+
* @param {Buffer|string|Readable|Array<Buffer|string>} data
|
|
74
|
+
* @param {object} options
|
|
75
|
+
* @param {(error: Error|null, result?: string|null, statusCode?: number,
|
|
76
|
+
* headers?: import('node:http').IncomingHttpHeaders) => void} callback
|
|
77
|
+
* @returns {void}
|
|
78
|
+
*/
|
|
79
|
+
function request (data, options, callback) {
|
|
80
|
+
const { signal } = options
|
|
81
|
+
if (signal?.aborted) return callback(getAbortError(signal))
|
|
82
|
+
|
|
83
|
+
if (data instanceof Readable) {
|
|
84
|
+
bufferReadable(data, signal, (error, bufferedData) => {
|
|
85
|
+
if (error) return callback(error)
|
|
86
|
+
requestBuffered(bufferedData, options, callback)
|
|
87
|
+
})
|
|
88
|
+
return
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
requestBuffered(data, options, callback)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Applies the Test Optimization retry policy to replayable request data.
|
|
96
|
+
*
|
|
97
|
+
* @param {Buffer|string|Array<Buffer|string>} data
|
|
98
|
+
* @param {object} options
|
|
99
|
+
* @param {(error: Error|null, result?: string|null, statusCode?: number,
|
|
100
|
+
* headers?: import('node:http').IncomingHttpHeaders) => void} callback
|
|
101
|
+
* @returns {void}
|
|
102
|
+
*/
|
|
103
|
+
function requestBuffered (data, options, callback) {
|
|
104
|
+
const { signal } = options
|
|
105
|
+
const timeout = options.timeout || 2000
|
|
106
|
+
let retryTimer
|
|
107
|
+
let settled = false
|
|
108
|
+
|
|
109
|
+
const complete = (error, result, statusCode, headers) => {
|
|
110
|
+
if (settled) return
|
|
111
|
+
settled = true
|
|
112
|
+
clearTimeout(retryTimer)
|
|
113
|
+
signal?.removeEventListener('abort', onAbort)
|
|
114
|
+
callback(error, result, statusCode, headers)
|
|
115
|
+
}
|
|
116
|
+
const onAbort = () => complete(getAbortError(signal))
|
|
117
|
+
|
|
118
|
+
signal?.addEventListener('abort', onAbort, { once: true })
|
|
119
|
+
if (signal?.aborted) return onAbort()
|
|
120
|
+
|
|
121
|
+
/** @param {number} attemptIndex */
|
|
122
|
+
const attempt = attemptIndex => {
|
|
123
|
+
if (settled) return
|
|
124
|
+
if (signal?.aborted) return onAbort()
|
|
125
|
+
|
|
126
|
+
const deadline = options.deadline
|
|
127
|
+
const remaining = deadline === undefined ? Infinity : deadline - Date.now()
|
|
128
|
+
if (remaining <= 0) {
|
|
129
|
+
const error = new Error('Test Optimization request reached its finalization deadline')
|
|
130
|
+
error.code = 'ERR_DD_TEST_OPTIMIZATION_FLUSH_TIMEOUT'
|
|
131
|
+
complete(error)
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (deadline !== undefined && !commonRequest.writable) {
|
|
136
|
+
retryTimer = setTimeout(attempt, Math.min(50, remaining), attemptIndex)
|
|
137
|
+
retryTimer.unref?.()
|
|
138
|
+
return
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const attemptOptions = {
|
|
142
|
+
...options,
|
|
143
|
+
headers: options.headers ? { ...options.headers } : undefined,
|
|
144
|
+
retry: false,
|
|
145
|
+
}
|
|
146
|
+
if (deadline !== undefined) attemptOptions.timeout = Math.max(1, Math.min(timeout, remaining))
|
|
147
|
+
|
|
148
|
+
commonRequest(data, attemptOptions, (error, result, statusCode, headers) => {
|
|
149
|
+
if (settled) return
|
|
150
|
+
if (!error) {
|
|
151
|
+
complete(null, result, statusCode, headers)
|
|
152
|
+
return
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const responseStatus = statusCode ?? error.status
|
|
156
|
+
const isRetriableHttpError = options.deadline !== undefined &&
|
|
157
|
+
(responseStatus === 429 || responseStatus >= 500)
|
|
158
|
+
if (options.retry === false || (attemptIndex >= getMaxAttempts(attemptOptions) &&
|
|
159
|
+
(isRetriableNetworkError(error) || isRetriableHttpError))) {
|
|
160
|
+
complete(error, result, statusCode, headers)
|
|
161
|
+
return
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (!isRetriableNetworkError(error) && !isRetriableHttpError) {
|
|
165
|
+
complete(error, result, statusCode, headers)
|
|
166
|
+
return
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
let retryDelay
|
|
170
|
+
if (responseStatus === 429) {
|
|
171
|
+
const resetDelay = getRateLimitResetDelay(headers)
|
|
172
|
+
if (Number.isFinite(resetDelay)) {
|
|
173
|
+
const retryRemaining = options.deadline - Date.now()
|
|
174
|
+
if (resetDelay > RATE_LIMIT_MAX_WAIT_MS || resetDelay >= retryRemaining) {
|
|
175
|
+
complete(error, result, statusCode, headers)
|
|
176
|
+
return
|
|
177
|
+
}
|
|
178
|
+
retryDelay = resetDelay
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
let delay = retryDelay ?? getRetryDelay(attemptOptions, attemptIndex)
|
|
183
|
+
if (options.deadline !== undefined && retryDelay === undefined) {
|
|
184
|
+
const retryRemaining = options.deadline - Date.now()
|
|
185
|
+
if (retryRemaining <= 0) {
|
|
186
|
+
complete(error, result, statusCode, headers)
|
|
187
|
+
return
|
|
188
|
+
}
|
|
189
|
+
const retryAttemptTimeout = timeout < retryRemaining ? timeout : Math.ceil(retryRemaining / 2)
|
|
190
|
+
delay = Math.min(delay, Math.max(0, retryRemaining - retryAttemptTimeout))
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
retryTimer = setTimeout(attempt, delay, attemptIndex + 1)
|
|
194
|
+
retryTimer.unref?.()
|
|
195
|
+
})
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
attempt(1)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
module.exports = request
|