dd-trace 6.12.0 → 6.14.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/LICENSE-3rdparty.csv +3 -1
- package/ci/init.js +8 -3
- package/ci/test-optimization-validation/ci-package-scripts.js +2 -2
- package/ci/test-optimization-validation/ci-remediation.js +6 -6
- package/ci/test-optimization-validation/command-blocker.js +7 -5
- package/ci/test-optimization-validation/framework-adapters/cucumber.js +1 -1
- package/ci/test-optimization-validation/framework-adapters/cypress.js +9 -11
- package/ci/test-optimization-validation/framework-adapters/vitest.js +3 -3
- package/ci/test-optimization-validation/package-check.js +1 -1
- package/ci/test-optimization-validation/report-writer.js +7 -5
- package/ci/test-optimization-validation/scenarios/ci-wiring.js +5 -4
- package/ci/vitest-no-worker-init-runner-setup.mjs +8 -0
- package/ci/vitest-no-worker-init-setup.mjs +13 -13
- package/ci/vitest-no-worker-init-v5-setup.mjs +4 -0
- package/index.d.ts +127 -26
- package/package.json +22 -19
- package/packages/datadog-esbuild/index.js +3 -1
- package/packages/datadog-instrumentations/src/bunyan.js +4 -2
- package/packages/datadog-instrumentations/src/cypress-config.js +19 -17
- package/packages/datadog-instrumentations/src/fs.js +24 -20
- package/packages/datadog-instrumentations/src/google-cloud-vertexai.js +2 -2
- package/packages/datadog-instrumentations/src/graphql.js +16 -16
- package/packages/datadog-instrumentations/src/helpers/bunyan.js +12 -3
- package/packages/datadog-instrumentations/src/helpers/graphql-jit-runtime.js +504 -0
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +266 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +1 -2
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +95 -30
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +0 -25
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql-jit.js +83 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql.js +85 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/mercurius.js +30 -12
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +13 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +279 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +12 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +562 -27
- package/packages/datadog-instrumentations/src/jest.js +165 -38
- package/packages/datadog-instrumentations/src/knex.js +76 -0
- package/packages/datadog-instrumentations/src/mariadb-bundle.js +1597 -0
- package/packages/datadog-instrumentations/src/mariadb.js +13 -0
- package/packages/datadog-instrumentations/src/mocha/common.js +59 -2
- package/packages/datadog-instrumentations/src/mocha/main.js +25 -15
- package/packages/datadog-instrumentations/src/mocha/utils.js +2 -1
- package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +98 -0
- package/packages/datadog-instrumentations/src/mocha/worker.js +12 -8
- package/packages/datadog-instrumentations/src/net.js +14 -17
- package/packages/datadog-instrumentations/src/next.js +212 -62
- package/packages/datadog-instrumentations/src/openai.js +42 -40
- package/packages/datadog-instrumentations/src/pg.js +15 -0
- package/packages/datadog-instrumentations/src/pino.js +15 -4
- package/packages/datadog-instrumentations/src/playwright.js +137 -27
- package/packages/datadog-instrumentations/src/rum-browser-scripts.js +40 -0
- package/packages/datadog-instrumentations/src/sequelize.js +94 -2
- package/packages/datadog-instrumentations/src/undici.js +53 -4
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +44 -7
- package/packages/datadog-instrumentations/src/vitest-main.js +92 -9
- package/packages/datadog-instrumentations/src/vitest-worker.js +82 -14
- package/packages/datadog-instrumentations/src/webdriverio.js +986 -1
- package/packages/datadog-instrumentations/src/winston.js +6 -3
- package/packages/datadog-plugin-amqp10/src/consumer.js +3 -1
- package/packages/datadog-plugin-amqp10/src/producer.js +3 -1
- package/packages/datadog-plugin-amqp10/src/util.js +2 -1
- package/packages/datadog-plugin-avsc/src/schema_iterator.js +6 -1
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +16 -5
- package/packages/datadog-plugin-cassandra-driver/src/index.js +8 -3
- package/packages/datadog-plugin-cucumber/src/index.js +0 -2
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +262 -48
- package/packages/datadog-plugin-cypress/src/support.js +1 -0
- package/packages/datadog-plugin-google-cloud-pubsub/src/client.js +4 -1
- package/packages/datadog-plugin-graphql/src/execute.js +998 -196
- package/packages/datadog-plugin-graphql/src/index.js +27 -7
- package/packages/datadog-plugin-graphql/src/jit.js +136 -0
- package/packages/datadog-plugin-graphql/src/request.js +18 -27
- package/packages/datadog-plugin-graphql/src/resolve-error.js +108 -0
- package/packages/datadog-plugin-graphql/src/tools/signature.js +1 -2
- package/packages/datadog-plugin-graphql/src/tools/transforms.js +45 -3
- package/packages/datadog-plugin-graphql/src/utils.js +208 -120
- package/packages/datadog-plugin-graphql/src/validate.js +10 -23
- package/packages/datadog-plugin-http/src/client.js +2 -15
- package/packages/datadog-plugin-http2/src/client.js +2 -15
- package/packages/datadog-plugin-jest/src/index.js +0 -6
- package/packages/datadog-plugin-langchain/src/tracing.js +7 -0
- package/packages/datadog-plugin-mocha/src/index.js +403 -75
- package/packages/datadog-plugin-mongodb-core/src/query.js +6 -0
- package/packages/datadog-plugin-mysql/src/index.js +41 -15
- package/packages/datadog-plugin-net/src/tcp.js +3 -1
- package/packages/datadog-plugin-next/src/index.js +63 -29
- package/packages/datadog-plugin-openai-agents/src/integration.js +15 -11
- package/packages/datadog-plugin-playwright/src/index.js +176 -28
- package/packages/datadog-plugin-undici/src/index.js +29 -56
- package/packages/datadog-plugin-vitest/src/index.js +6 -12
- package/packages/dd-trace/src/aiguard/evaluation.js +96 -21
- package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +2 -3
- package/packages/dd-trace/src/aiguard/messages/anthropic.js +29 -10
- package/packages/dd-trace/src/aiguard/messages/openai.js +8 -1
- package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +20 -6
- package/packages/dd-trace/src/aiguard/noop.js +9 -1
- package/packages/dd-trace/src/aiguard/redaction.js +183 -0
- package/packages/dd-trace/src/aiguard/sdk.js +7 -2
- package/packages/dd-trace/src/aiguard/tags.js +2 -0
- package/packages/dd-trace/src/appsec/api_security/index.js +23 -6
- package/packages/dd-trace/src/appsec/api_security/sampler.js +99 -40
- package/packages/dd-trace/src/appsec/graphql.js +6 -2
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +3 -3
- package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +2 -1
- package/packages/dd-trace/src/appsec/iast/telemetry/span-tags.js +8 -2
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +11 -36
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +10 -9
- package/packages/dd-trace/src/appsec/lambda.js +44 -26
- package/packages/dd-trace/src/appsec/rule_manager.js +2 -0
- package/packages/dd-trace/src/ci-visibility/encode/json-encoder.js +32 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +8 -8
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +6 -4
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +20 -4
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +7 -150
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +7 -6
- package/packages/dd-trace/src/ci-visibility/exporters/agents.js +54 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/writer.js +1 -2
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +397 -234
- package/packages/dd-trace/src/ci-visibility/exporters/limits.js +9 -0
- package/packages/dd-trace/src/ci-visibility/exporters/request.js +323 -31
- package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +42 -0
- package/packages/dd-trace/src/ci-visibility/final-flush.js +21 -0
- package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +57 -18
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +0 -3
- package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +164 -71
- package/packages/dd-trace/src/ci-visibility/requests/video-request.js +176 -0
- package/packages/dd-trace/src/ci-visibility/telemetry.js +10 -7
- package/packages/dd-trace/src/ci-visibility/test-video.js +61 -0
- package/packages/dd-trace/src/config/generated-config-types.d.ts +10 -0
- package/packages/dd-trace/src/config/index.js +75 -14
- package/packages/dd-trace/src/config/parsers.js +11 -1
- package/packages/dd-trace/src/config/stable.js +3 -3
- package/packages/dd-trace/src/config/supported-configurations.json +47 -2
- package/packages/dd-trace/src/crashtracking/crashtracker.js +82 -18
- package/packages/dd-trace/src/debugger/config.js +12 -1
- package/packages/dd-trace/src/debugger/constants.js +1 -0
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +2 -1
- package/packages/dd-trace/src/debugger/devtools_client/request-options.js +33 -0
- package/packages/dd-trace/src/debugger/devtools_client/send.js +7 -39
- package/packages/dd-trace/src/debugger/devtools_client/status.js +11 -6
- package/packages/dd-trace/src/debugger/devtools_client/tags.js +41 -0
- package/packages/dd-trace/src/debugger/index.js +23 -3
- package/packages/dd-trace/src/dogstatsd.js +532 -71
- package/packages/dd-trace/src/encode/0.4-cross-payload.js +170 -0
- package/packages/dd-trace/src/encode/0.4.js +5 -5
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +9 -8
- package/packages/dd-trace/src/evp_proxy/direct.js +27 -23
- package/packages/dd-trace/src/exporters/agent/index.js +24 -3
- package/packages/dd-trace/src/exporters/agent/writer.js +28 -11
- package/packages/dd-trace/src/exporters/agentless/index.js +7 -9
- package/packages/dd-trace/src/exporters/agentless/writer.js +100 -118
- package/packages/dd-trace/src/exporters/common/agents.js +28 -6
- package/packages/dd-trace/src/exporters/common/buffering-exporter.js +2 -2
- package/packages/dd-trace/src/exporters/common/final-flush-request-tracker.js +191 -0
- package/packages/dd-trace/src/exporters/common/request.js +31 -11
- package/packages/dd-trace/src/exporters/common/url.js +40 -1
- package/packages/dd-trace/src/exporters/common/writer.js +31 -7
- package/packages/dd-trace/src/exporters/span-stats/index.js +23 -4
- package/packages/dd-trace/src/external-logger/src/index.js +4 -3
- package/packages/dd-trace/src/flush.js +105 -0
- package/packages/dd-trace/src/id.js +14 -0
- package/packages/dd-trace/src/index.js +5 -2
- package/packages/dd-trace/src/llmobs/experiments/client.js +4 -0
- package/packages/dd-trace/src/llmobs/experiments/dataset.js +52 -2
- package/packages/dd-trace/src/llmobs/experiments/experiment.js +420 -105
- package/packages/dd-trace/src/llmobs/experiments/index.js +54 -42
- package/packages/dd-trace/src/llmobs/experiments/noop.js +39 -8
- package/packages/dd-trace/src/llmobs/experiments/result.js +1 -0
- package/packages/dd-trace/src/llmobs/experiments/util.js +21 -0
- package/packages/dd-trace/src/llmobs/index.js +111 -48
- package/packages/dd-trace/src/llmobs/noop.js +2 -1
- package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +22 -71
- package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +23 -13
- package/packages/dd-trace/src/llmobs/plugins/anthropic/util.js +8 -4
- package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/index.js +6 -3
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +7 -0
- package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +13 -6
- package/packages/dd-trace/src/llmobs/plugins/openai-agents/utils.js +5 -7
- package/packages/dd-trace/src/llmobs/util.js +0 -33
- package/packages/dd-trace/src/llmobs/writers/base.js +106 -53
- package/packages/dd-trace/src/log/index.js +7 -1
- package/packages/dd-trace/src/log-submission/log-submission-plugin.js +249 -0
- package/packages/dd-trace/src/noop/span.js +5 -0
- package/packages/dd-trace/src/openfeature/writers/base.js +14 -12
- package/packages/dd-trace/src/openfeature/writers/exposures.js +8 -1
- package/packages/dd-trace/src/openfeature/writers/util.js +1 -1
- package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +45 -3
- package/packages/dd-trace/src/opentelemetry/logs/index.js +9 -25
- package/packages/dd-trace/src/opentelemetry/logs/logger_provider.js +7 -4
- package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +14 -4
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +18 -23
- package/packages/dd-trace/src/opentelemetry/metrics/meter_provider.js +8 -0
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_http_metric_exporter.js +18 -6
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +14 -2
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +25 -5
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +61 -25
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +19 -4
- package/packages/dd-trace/src/opentelemetry/resource-attributes.js +55 -0
- package/packages/dd-trace/src/opentelemetry/trace/index.js +6 -1
- package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +10 -0
- package/packages/dd-trace/src/opentelemetry/tracer.js +26 -2
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +494 -351
- package/packages/dd-trace/src/opentracing/span.js +28 -0
- package/packages/dd-trace/src/otel-thread-ctx.js +23 -5
- package/packages/dd-trace/src/payload-tagging/index.js +2 -1
- package/packages/dd-trace/src/plugin_manager.js +2 -0
- package/packages/dd-trace/src/plugins/ci_plugin.js +1 -6
- package/packages/dd-trace/src/plugins/tracing.js +12 -10
- package/packages/dd-trace/src/plugins/util/ci.js +3 -1
- package/packages/dd-trace/src/plugins/util/inferred_proxy.js +16 -15
- package/packages/dd-trace/src/plugins/util/status-validator.js +98 -0
- package/packages/dd-trace/src/plugins/util/test.js +157 -41
- package/packages/dd-trace/src/plugins/util/url.js +2 -2
- package/packages/dd-trace/src/plugins/util/web.js +6 -74
- package/packages/dd-trace/src/profiling/config.js +1 -0
- package/packages/dd-trace/src/profiling/oom.js +11 -5
- package/packages/dd-trace/src/profiling/profiler.js +30 -13
- package/packages/dd-trace/src/profiling/profilers/events.js +3 -3
- package/packages/dd-trace/src/profiling/profilers/space.js +38 -11
- package/packages/dd-trace/src/profiling/profilers/wall.js +14 -14
- package/packages/dd-trace/src/profiling/webspan-utils.js +7 -4
- package/packages/dd-trace/src/proxy.js +108 -10
- package/packages/dd-trace/src/remote_config/index.js +319 -101
- package/packages/dd-trace/src/remote_config/scheduler.js +37 -12
- package/packages/dd-trace/src/runtime_metrics/client.js +37 -8
- package/packages/dd-trace/src/runtime_metrics/index.js +5 -0
- package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +10 -1
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +22 -7
- package/packages/dd-trace/src/sampling_rule.js +36 -9
- package/packages/dd-trace/src/serverless/telemetry-delivery-tracker.js +51 -0
- package/packages/dd-trace/src/serverless/vercel.js +155 -0
- package/packages/dd-trace/src/serverless.js +45 -21
- package/packages/dd-trace/src/service-naming/schemas/util.js +1 -1
- package/packages/dd-trace/src/span_stats.js +64 -31
- package/packages/dd-trace/src/startup-log.js +3 -2
- package/packages/dd-trace/src/telemetry/agentless-url.js +28 -0
- package/packages/dd-trace/src/telemetry/send-data.js +18 -16
- package/packages/dd-trace/src/telemetry/session-propagation.js +3 -2
- package/packages/dd-trace/src/tracer.js +26 -5
- package/packages/dd-trace/src/tracer_metadata.js +11 -3
- package/packages/dd-trace/src/web-tags-cache.js +67 -13
- package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/protobufjs/index.js +1 -1
- package/vendor/dist/protobufjs/minimal/index.js +1 -1
- package/packages/datadog-instrumentations/src/playwright-browser-scripts.js +0 -27
- package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +0 -53
- package/packages/dd-trace/src/encode/agentless-json.js +0 -209
|
@@ -26,6 +26,15 @@ const finishedRegistry = createRegistry('finished')
|
|
|
26
26
|
let OTEL_ENABLED = false
|
|
27
27
|
const ALLOWED = new Set(['string', 'number', 'boolean'])
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* @typedef {object} RecordedException
|
|
31
|
+
* @property {string} message
|
|
32
|
+
* @property {string} [name]
|
|
33
|
+
* @property {string} [stack]
|
|
34
|
+
* @typedef {string | number | boolean} SpanEventAttributeScalar
|
|
35
|
+
* @typedef {SpanEventAttributeScalar | string[] | number[] | boolean[]} SpanEventAttributeValue
|
|
36
|
+
*/
|
|
37
|
+
|
|
29
38
|
const integrationCounters = {
|
|
30
39
|
spans_created: {},
|
|
31
40
|
spans_finished: {},
|
|
@@ -306,6 +315,25 @@ class DatadogSpan {
|
|
|
306
315
|
this._events.push(event)
|
|
307
316
|
}
|
|
308
317
|
|
|
318
|
+
/**
|
|
319
|
+
* @param {RecordedException} exception
|
|
320
|
+
* @param {Record<string, SpanEventAttributeValue>} [attributes]
|
|
321
|
+
*/
|
|
322
|
+
recordException (exception, attributes) {
|
|
323
|
+
const { message, name, stack } = exception
|
|
324
|
+
const eventAttributes = { ...attributes }
|
|
325
|
+
eventAttributes['exception.message'] ??= message
|
|
326
|
+
|
|
327
|
+
if (typeof name === 'string') {
|
|
328
|
+
eventAttributes['exception.type'] ??= name
|
|
329
|
+
}
|
|
330
|
+
if (typeof stack === 'string') {
|
|
331
|
+
eventAttributes['exception.stacktrace'] ??= stack
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
this.addEvent('exception', eventAttributes)
|
|
335
|
+
}
|
|
336
|
+
|
|
309
337
|
finish (finishTime) {
|
|
310
338
|
if (this._duration !== undefined) {
|
|
311
339
|
return
|
|
@@ -105,6 +105,10 @@ const THREAD_ID = String(threadId)
|
|
|
105
105
|
// time onEnter activates the span, and cleared on span finish, which
|
|
106
106
|
// is how a pendingEndpoints waiter recognizes a record it must no
|
|
107
107
|
// longer append to.
|
|
108
|
+
// webTags: the web-server tag bag the record's endpoint comes from, or is
|
|
109
|
+
// waiting on; undefined while the span has no web-server ancestry.
|
|
110
|
+
// Distinguishes a stale announcement from a live one when a nearer
|
|
111
|
+
// web-server span takes over.
|
|
108
112
|
const CachedSym = Symbol('OtelThreadCtx.cached')
|
|
109
113
|
|
|
110
114
|
let started = false
|
|
@@ -137,6 +141,7 @@ function getOrBuildContext (span) {
|
|
|
137
141
|
span[CachedSym] = cached
|
|
138
142
|
}
|
|
139
143
|
cached.context = new ThreadContext(traceId, spanId, attrs)
|
|
144
|
+
cached.webTags = webTags
|
|
140
145
|
if (endpoint === undefined) awaitEndpoint(webTags, cached)
|
|
141
146
|
return cached.context
|
|
142
147
|
}
|
|
@@ -227,21 +232,34 @@ function onEndpointResolved (span) {
|
|
|
227
232
|
if (endpoint === undefined) return
|
|
228
233
|
pendingEndpoints.delete(webTags)
|
|
229
234
|
for (const cached of waiting) {
|
|
230
|
-
|
|
235
|
+
// A record whose span has since been attributed to a nearer web-server span
|
|
236
|
+
// enlisted again under that bag, and this one is no longer its endpoint.
|
|
237
|
+
if (cached.context !== undefined && cached.webTags === webTags) appendEndpoint(cached.context, endpoint)
|
|
231
238
|
}
|
|
232
239
|
}
|
|
233
240
|
|
|
234
|
-
//
|
|
235
|
-
//
|
|
236
|
-
//
|
|
237
|
-
//
|
|
241
|
+
// webTagsCache has changed which request it attributes this span to: the span
|
|
242
|
+
// itself or an ancestor was recognized as a web-server span, giving a record
|
|
243
|
+
// built without any web-server ancestry its first endpoint, or a nearer
|
|
244
|
+
// web-server span superseded the one this record is showing. Give the record
|
|
245
|
+
// the new request's endpoint if it has settled, or wait for its announcement.
|
|
238
246
|
function onWebTagsResolved (span) {
|
|
239
247
|
if (!started) return
|
|
240
248
|
const cached = span[CachedSym]
|
|
241
249
|
if (cached === undefined || cached.context === undefined) return
|
|
242
250
|
const webTags = webTagsCache.getCachedWebTags(span)
|
|
251
|
+
if (webTags === cached.webTags) return
|
|
252
|
+
// Recorded so that an announcement for the bag this record was previously
|
|
253
|
+
// waiting on no longer applies to it.
|
|
254
|
+
cached.webTags = webTags
|
|
243
255
|
const endpoint = finalEndpoint(webTags)
|
|
244
256
|
if (endpoint === undefined) {
|
|
257
|
+
// A record that already shows the outer request's endpoint goes on showing
|
|
258
|
+
// it until the nearer one settles: the record buffer is append-only, so
|
|
259
|
+
// there is no way to take the attribute back, and rebuilding the
|
|
260
|
+
// ThreadContext would strand every async-context frame holding this one.
|
|
261
|
+
// The work is still nested in the outer request, which makes that the least
|
|
262
|
+
// wrong of the values available.
|
|
245
263
|
awaitEndpoint(webTags, cached)
|
|
246
264
|
} else {
|
|
247
265
|
appendEndpoint(cached.context, endpoint)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const createRfdc = require('../../../../vendor/dist/rfdc')
|
|
4
|
+
const rfdc = createRfdc({ proto: false, circles: false })
|
|
4
5
|
|
|
5
6
|
const {
|
|
6
7
|
PAYLOAD_TAG_REQUEST_PREFIX,
|
|
@@ -165,6 +165,7 @@ module.exports = class PluginManager {
|
|
|
165
165
|
codeOriginForSpans,
|
|
166
166
|
dbmPropagationMode,
|
|
167
167
|
dsmEnabled,
|
|
168
|
+
DD_TRACE_HTTP_CLIENT_ERROR_STATUSES,
|
|
168
169
|
DD_TRACE_HTTP_SERVER_ERROR_STATUSES,
|
|
169
170
|
clientIpEnabled,
|
|
170
171
|
clientIpHeader,
|
|
@@ -195,6 +196,7 @@ module.exports = class PluginManager {
|
|
|
195
196
|
site,
|
|
196
197
|
url,
|
|
197
198
|
headers: headerTags || [],
|
|
199
|
+
DD_TRACE_HTTP_CLIENT_ERROR_STATUSES,
|
|
198
200
|
DD_TRACE_HTTP_SERVER_ERROR_STATUSES,
|
|
199
201
|
clientIpHeader,
|
|
200
202
|
DD_TEST_SESSION_NAME,
|
|
@@ -728,12 +728,7 @@ module.exports = class CiPlugin extends Plugin {
|
|
|
728
728
|
this._bufferWorkerTrace(missingTestSuite, trace)
|
|
729
729
|
return
|
|
730
730
|
}
|
|
731
|
-
|
|
732
|
-
if (exporter.exportTraceWithDeferredTestSuite) {
|
|
733
|
-
exporter.exportTraceWithDeferredTestSuite(trace)
|
|
734
|
-
} else {
|
|
735
|
-
exporter.export(trace)
|
|
736
|
-
}
|
|
731
|
+
this.tracer._exporter.export(trace)
|
|
737
732
|
}
|
|
738
733
|
|
|
739
734
|
/**
|
|
@@ -243,19 +243,21 @@ class TracingPlugin extends Plugin {
|
|
|
243
243
|
serviceSource = undefined
|
|
244
244
|
}
|
|
245
245
|
|
|
246
|
+
const tags = {
|
|
247
|
+
[COMPONENT]: component,
|
|
248
|
+
'service.name': serviceName || tracer._service,
|
|
249
|
+
'resource.name': resource,
|
|
250
|
+
'span.kind': kind,
|
|
251
|
+
'span.type': type,
|
|
252
|
+
...meta,
|
|
253
|
+
...metrics,
|
|
254
|
+
}
|
|
255
|
+
if (serviceSource !== undefined && !Object.hasOwn(tags, SVC_SRC_KEY)) tags[SVC_SRC_KEY] = serviceSource
|
|
256
|
+
|
|
246
257
|
const span = tracer.startSpan(name, {
|
|
247
258
|
startTime,
|
|
248
259
|
childOf,
|
|
249
|
-
tags
|
|
250
|
-
[COMPONENT]: component,
|
|
251
|
-
'service.name': serviceName || tracer._service,
|
|
252
|
-
'resource.name': resource,
|
|
253
|
-
'span.kind': kind,
|
|
254
|
-
'span.type': type,
|
|
255
|
-
...(serviceSource === undefined ? undefined : { [SVC_SRC_KEY]: serviceSource }),
|
|
256
|
-
...meta,
|
|
257
|
-
...metrics,
|
|
258
|
-
},
|
|
260
|
+
tags,
|
|
259
261
|
integrationName: integrationName || component,
|
|
260
262
|
links: childOf?._links,
|
|
261
263
|
})
|
|
@@ -45,7 +45,9 @@ function parseEmailAndName (emailAndName) {
|
|
|
45
45
|
}
|
|
46
46
|
let name = ''
|
|
47
47
|
let email = ''
|
|
48
|
-
|
|
48
|
+
// Untrusted git author metadata (a fork PR controls it). The anchor fixes the start position, the
|
|
49
|
+
// email classes cannot overlap around `@`, and the slice bounds the optional name part.
|
|
50
|
+
const matchNameAndEmail = emailAndName.slice(0, 1024).match(/^(?:"?([^"]*)"?\s)?<?([^\s@]+@[^\s>]+)>?/)
|
|
49
51
|
if (matchNameAndEmail) {
|
|
50
52
|
name = matchNameAndEmail[1]
|
|
51
53
|
email = matchNameAndEmail[2]
|
|
@@ -69,26 +69,27 @@ function createInferredProxySpan (headers, childOf, tracer, reqCtx, traceCtx, co
|
|
|
69
69
|
|
|
70
70
|
log.debug('Successfully extracted inferred span info %s for proxy:', proxyContext, proxyContext.proxySystemName)
|
|
71
71
|
|
|
72
|
+
const meta = {
|
|
73
|
+
service: proxyContext.domainName || tracer._config.service,
|
|
74
|
+
component: proxySpanInfo.component,
|
|
75
|
+
[SPAN_TYPE]: 'web',
|
|
76
|
+
[SPAN_KIND]: 'server',
|
|
77
|
+
[HTTP_METHOD]: proxyContext.method,
|
|
78
|
+
[HTTP_URL]: 'https://' + proxyContext.domainName + proxyContext.path,
|
|
79
|
+
stage: proxyContext.stage,
|
|
80
|
+
region: proxyContext.region,
|
|
81
|
+
}
|
|
82
|
+
if (proxyContext.resourcePath) meta[HTTP_ROUTE] = proxyContext.resourcePath
|
|
83
|
+
if (proxyContext.accountId) meta.account_id = proxyContext.accountId
|
|
84
|
+
if (proxyContext.apiId) meta.apiid = proxyContext.apiId
|
|
85
|
+
if (proxyContext.awsUser) meta.aws_user = proxyContext.awsUser
|
|
86
|
+
|
|
72
87
|
const span = startSpanHelper(tracer, proxySpanInfo.spanName, {
|
|
73
88
|
childOf,
|
|
74
89
|
type: 'web',
|
|
75
90
|
startTime: proxyContext.requestTime,
|
|
76
91
|
integrationName: proxySpanInfo.component,
|
|
77
|
-
meta
|
|
78
|
-
service: proxyContext.domainName || tracer._config.service,
|
|
79
|
-
component: proxySpanInfo.component,
|
|
80
|
-
[SPAN_TYPE]: 'web',
|
|
81
|
-
[SPAN_KIND]: 'server',
|
|
82
|
-
[HTTP_METHOD]: proxyContext.method,
|
|
83
|
-
[HTTP_URL]: 'https://' + proxyContext.domainName + proxyContext.path,
|
|
84
|
-
stage: proxyContext.stage,
|
|
85
|
-
region: proxyContext.region,
|
|
86
|
-
...(proxyContext.resourcePath && { [HTTP_ROUTE]: proxyContext.resourcePath }),
|
|
87
|
-
...(proxyContext.accountId && { account_id: proxyContext.accountId }),
|
|
88
|
-
...(proxyContext.apiId && { apiid: proxyContext.apiId }),
|
|
89
|
-
...(proxyContext.region && { region: proxyContext.region }),
|
|
90
|
-
...(proxyContext.awsUser && { aws_user: proxyContext.awsUser }),
|
|
91
|
-
},
|
|
92
|
+
meta,
|
|
92
93
|
}, traceCtx, config)
|
|
93
94
|
|
|
94
95
|
reqCtx.inferredProxySpan = span
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const log = require('../../log')
|
|
4
|
+
|
|
5
|
+
const statusCodeRangesPattern = /^[1-5]\d{2}(?:-[1-5]\d{2})?(?:,[1-5]\d{2}(?:-[1-5]\d{2})?)*$/
|
|
6
|
+
const whitespacePattern = /\s/g
|
|
7
|
+
const MAX_HTTP_STATUS_CODE = 599
|
|
8
|
+
|
|
9
|
+
const SERVER_ERROR_STATUSES = 'DD_TRACE_HTTP_SERVER_ERROR_STATUSES'
|
|
10
|
+
const CLIENT_ERROR_STATUSES = 'DD_TRACE_HTTP_CLIENT_ERROR_STATUSES'
|
|
11
|
+
|
|
12
|
+
/** @typedef {(code: number) => boolean} StatusValidator */
|
|
13
|
+
|
|
14
|
+
/** @type {StatusValidator} */
|
|
15
|
+
function isNotServerErrorCode (code) {
|
|
16
|
+
return code < 500
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** @type {StatusValidator} */
|
|
20
|
+
function isNotClientErrorCode (code) {
|
|
21
|
+
return code < 400 || code >= 500
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @param {{ validateStatus?: unknown, DD_TRACE_HTTP_SERVER_ERROR_STATUSES?: unknown }} config
|
|
26
|
+
* @returns {StatusValidator}
|
|
27
|
+
*/
|
|
28
|
+
function getServerStatusValidator (config) {
|
|
29
|
+
return getStatusValidator(config, SERVER_ERROR_STATUSES, '500-599', isNotServerErrorCode)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @param {{ validateStatus?: unknown, DD_TRACE_HTTP_CLIENT_ERROR_STATUSES?: unknown }} config
|
|
34
|
+
* @returns {StatusValidator}
|
|
35
|
+
*/
|
|
36
|
+
function getClientStatusValidator (config) {
|
|
37
|
+
return getStatusValidator(config, CLIENT_ERROR_STATUSES, '400-499', isNotClientErrorCode)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @param {Record<string, unknown>} config
|
|
42
|
+
* @param {string} optionName - Name of the configuration holding the error status ranges.
|
|
43
|
+
* @param {string} defaultRanges - Ranges covered by `defaultValidator`
|
|
44
|
+
* @param {StatusValidator} defaultValidator
|
|
45
|
+
* @returns {StatusValidator}
|
|
46
|
+
*/
|
|
47
|
+
function getStatusValidator (config, optionName, defaultRanges, defaultValidator) {
|
|
48
|
+
if (typeof config.validateStatus === 'function') {
|
|
49
|
+
return /** @type {StatusValidator} */ (config.validateStatus)
|
|
50
|
+
} else if (Object.hasOwn(config, 'validateStatus')) {
|
|
51
|
+
log.error('Expected `validateStatus` to be a function.')
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const ranges = config[optionName]
|
|
55
|
+
if (ranges === undefined) return defaultValidator
|
|
56
|
+
if (typeof ranges !== 'string') {
|
|
57
|
+
log.error('Expected `%s` to be a string.', optionName)
|
|
58
|
+
return defaultValidator
|
|
59
|
+
}
|
|
60
|
+
if (ranges === defaultRanges) return defaultValidator
|
|
61
|
+
|
|
62
|
+
const normalized = ranges.replaceAll(whitespacePattern, '')
|
|
63
|
+
if (normalized === defaultRanges) return defaultValidator
|
|
64
|
+
if (!statusCodeRangesPattern.test(normalized)) {
|
|
65
|
+
log.error('`%s` must contain comma-separated status codes or ranges from 100 to 599.', optionName)
|
|
66
|
+
return defaultValidator
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const errorStatusCodes = new Uint8Array(MAX_HTTP_STATUS_CODE + 1)
|
|
70
|
+
for (const range of normalized.split(',')) {
|
|
71
|
+
const separator = range.indexOf('-')
|
|
72
|
+
if (separator === -1) {
|
|
73
|
+
errorStatusCodes[Number(range)] = 1
|
|
74
|
+
continue
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const first = Number(range.slice(0, separator))
|
|
78
|
+
const second = Number(range.slice(separator + 1))
|
|
79
|
+
const start = Math.min(first, second)
|
|
80
|
+
const end = Math.max(first, second)
|
|
81
|
+
errorStatusCodes.fill(1, start, end + 1)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* @param {number} code
|
|
86
|
+
* @returns {boolean}
|
|
87
|
+
*/
|
|
88
|
+
function isValidStatusCode (code) {
|
|
89
|
+
return errorStatusCodes[code] !== 1
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return isValidStatusCode
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
module.exports = {
|
|
96
|
+
getClientStatusValidator,
|
|
97
|
+
getServerStatusValidator,
|
|
98
|
+
}
|
|
@@ -108,6 +108,9 @@ const TEST_SOURCE_FILE = 'test.source.file'
|
|
|
108
108
|
const TEST_SOURCE_START = 'test.source.start'
|
|
109
109
|
const TEST_FAILURE_SCREENSHOT_UPLOADED = 'test.failure_screenshot.uploaded'
|
|
110
110
|
const TEST_FAILURE_SCREENSHOT_UPLOAD_ERROR = 'test.failure_screenshot.upload_error'
|
|
111
|
+
const TEST_FAILURE_VIDEO_UPLOADED = 'test.failure_video.uploaded'
|
|
112
|
+
const TEST_FAILURE_VIDEO_UPLOAD_ERROR = 'test.failure_video.upload_error'
|
|
113
|
+
const TEST_FAILURE_VIDEO_SCOPE = 'test.failure_video.scope'
|
|
111
114
|
const LIBRARY_VERSION = 'library_version'
|
|
112
115
|
const TEST_COMMAND = 'test.command'
|
|
113
116
|
const TEST_MODULE = 'test.module'
|
|
@@ -127,7 +130,7 @@ const TEST_IS_MODIFIED = 'test.is_modified'
|
|
|
127
130
|
const TEST_HAS_DYNAMIC_NAME = '_dd.has_dynamic_name'
|
|
128
131
|
const CI_APP_ORIGIN = 'ciapp-test'
|
|
129
132
|
// eslint-disable-next-line no-control-regex
|
|
130
|
-
const TEST_OPTIMIZATION_NAME_CONTROL_RE = /[\u0000-\u0008\
|
|
133
|
+
const TEST_OPTIMIZATION_NAME_CONTROL_RE = /[\u0000-\u0008\v\f\u000E-\u001F\u007F-\u009F]/g
|
|
131
134
|
const TEST_OPTIMIZATION_NAME_WHITESPACE_RE = /\s+/g
|
|
132
135
|
|
|
133
136
|
// Matches patterns that are almost certainly runtime-generated values in test names:
|
|
@@ -474,6 +477,9 @@ module.exports = {
|
|
|
474
477
|
TEST_SOURCE_FILE,
|
|
475
478
|
TEST_FAILURE_SCREENSHOT_UPLOADED,
|
|
476
479
|
TEST_FAILURE_SCREENSHOT_UPLOAD_ERROR,
|
|
480
|
+
TEST_FAILURE_VIDEO_UPLOADED,
|
|
481
|
+
TEST_FAILURE_VIDEO_UPLOAD_ERROR,
|
|
482
|
+
TEST_FAILURE_VIDEO_SCOPE,
|
|
477
483
|
CI_APP_ORIGIN,
|
|
478
484
|
LIBRARY_VERSION,
|
|
479
485
|
JEST_WORKER_TRACE_PAYLOAD_CODE,
|
|
@@ -541,6 +547,7 @@ module.exports = {
|
|
|
541
547
|
getRelativeCoverageFiles,
|
|
542
548
|
getLineCoverageBitmap,
|
|
543
549
|
applySkippedCoverageToCoverage,
|
|
550
|
+
getTestCoverageLinesData,
|
|
544
551
|
getTestCoverageLinesPercentage,
|
|
545
552
|
resetCoverage,
|
|
546
553
|
mergeCoverage,
|
|
@@ -1359,6 +1366,34 @@ function getLineCoverageBitmap (lineCoverage, onlyCoveredLines = false) {
|
|
|
1359
1366
|
return bitmap
|
|
1360
1367
|
}
|
|
1361
1368
|
|
|
1369
|
+
function getLineCoverageBitmaps (lineCoverage) {
|
|
1370
|
+
let maxLine = 0
|
|
1371
|
+
const entries = Object.entries(lineCoverage)
|
|
1372
|
+
|
|
1373
|
+
for (const [line] of entries) {
|
|
1374
|
+
const lineNumber = Number(line)
|
|
1375
|
+
if (Number.isSafeInteger(lineNumber) && lineNumber > maxLine) {
|
|
1376
|
+
maxLine = lineNumber
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
if (maxLine === 0) return {}
|
|
1380
|
+
|
|
1381
|
+
const length = Math.ceil((maxLine + 1) / 8)
|
|
1382
|
+
const coveredBitmap = Buffer.alloc(length)
|
|
1383
|
+
const executableBitmap = Buffer.alloc(length)
|
|
1384
|
+
for (const [line, hits] of entries) {
|
|
1385
|
+
const lineNumber = Number(line)
|
|
1386
|
+
if (!Number.isSafeInteger(lineNumber) || lineNumber <= 0) continue
|
|
1387
|
+
|
|
1388
|
+
const byteIndex = lineNumber >> 3
|
|
1389
|
+
const bit = 1 << (lineNumber % 8)
|
|
1390
|
+
executableBitmap[byteIndex] |= bit
|
|
1391
|
+
if (hits) coveredBitmap[byteIndex] |= bit
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
return { coveredBitmap, executableBitmap }
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1362
1397
|
function mergeCoverageBitmaps (targetBitmap, bitmap) {
|
|
1363
1398
|
if (!targetBitmap) {
|
|
1364
1399
|
return Buffer.from(bitmap)
|
|
@@ -1419,16 +1454,6 @@ function getCoverageFileBitmap (bitmap) {
|
|
|
1419
1454
|
}
|
|
1420
1455
|
}
|
|
1421
1456
|
|
|
1422
|
-
function addCoverageFilesToMap (files, targetMap, rootDir) {
|
|
1423
|
-
for (const file of files) {
|
|
1424
|
-
const bitmap = getCoverageFileBitmap(file.bitmap)
|
|
1425
|
-
if (!bitmap) continue
|
|
1426
|
-
|
|
1427
|
-
const filename = rootDir ? getTestSuitePath(file.filename, rootDir) : file.filename
|
|
1428
|
-
targetMap.set(filename, mergeCoverageBitmaps(targetMap.get(filename), bitmap))
|
|
1429
|
-
}
|
|
1430
|
-
}
|
|
1431
|
-
|
|
1432
1457
|
function addSkippedCoverageToMap (skippedCoverage, targetMap) {
|
|
1433
1458
|
if (!skippedCoverage) return
|
|
1434
1459
|
|
|
@@ -1439,23 +1464,57 @@ function addSkippedCoverageToMap (skippedCoverage, targetMap) {
|
|
|
1439
1464
|
}
|
|
1440
1465
|
}
|
|
1441
1466
|
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1467
|
+
/**
|
|
1468
|
+
* Calculates line coverage and optionally returns executable-line coverage files in the same traversal.
|
|
1469
|
+
* @param {object} coverage
|
|
1470
|
+
* @param {object} [skippedCoverage]
|
|
1471
|
+
* @param {string} [rootDir]
|
|
1472
|
+
* @param {boolean} [includeExecutableFiles]
|
|
1473
|
+
* @returns {{ percentage: number, executableFiles?: Array<{ filename: string, bitmap: Buffer }> }}
|
|
1474
|
+
*/
|
|
1475
|
+
function getTestCoverageLinesData (coverage, skippedCoverage, rootDir, includeExecutableFiles = false) {
|
|
1476
|
+
const coverageMap = getCoverageMap(coverage)
|
|
1477
|
+
const skippedCoverageByFilename = getSkippedCoverageByFilename(skippedCoverage)
|
|
1478
|
+
const coverageByFilename = new Map()
|
|
1479
|
+
const executableFiles = includeExecutableFiles ? [] : undefined
|
|
1445
1480
|
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1481
|
+
for (const filename of coverageMap.files()) {
|
|
1482
|
+
const fileCoverage = coverageMap.fileCoverageFor(filename)
|
|
1483
|
+
const { coveredBitmap, executableBitmap } = getLineCoverageBitmaps(fileCoverage.getLineCoverage())
|
|
1484
|
+
if (!executableBitmap) continue
|
|
1485
|
+
|
|
1486
|
+
const relativeFilename = rootDir ? getTestSuitePath(filename, rootDir) : filename
|
|
1487
|
+
const existingCoverage = coverageByFilename.get(relativeFilename)
|
|
1488
|
+
if (existingCoverage) {
|
|
1489
|
+
existingCoverage.coveredBitmap = mergeCoverageBitmaps(existingCoverage.coveredBitmap, coveredBitmap)
|
|
1490
|
+
existingCoverage.executableBitmap = mergeCoverageBitmaps(existingCoverage.executableBitmap, executableBitmap)
|
|
1491
|
+
} else {
|
|
1492
|
+
coverageByFilename.set(relativeFilename, { coveredBitmap, executableBitmap })
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1449
1495
|
|
|
1450
1496
|
let totalExecutableLines = 0
|
|
1451
1497
|
let totalCoveredLines = 0
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1498
|
+
for (const [filename, { coveredBitmap, executableBitmap }] of coverageByFilename) {
|
|
1499
|
+
const skippedBitmap = skippedCoverageByFilename.get(filename)
|
|
1500
|
+
const combinedCoveredBitmap = skippedBitmap
|
|
1501
|
+
? mergeCoverageBitmaps(coveredBitmap, skippedBitmap)
|
|
1502
|
+
: coveredBitmap
|
|
1503
|
+
totalExecutableLines += countBitmapBits(executableBitmap)
|
|
1504
|
+
totalCoveredLines += countCoveredExecutableBits(combinedCoveredBitmap, executableBitmap)
|
|
1505
|
+
if (executableFiles) {
|
|
1506
|
+
executableFiles.push({ filename, bitmap: executableBitmap })
|
|
1507
|
+
}
|
|
1456
1508
|
}
|
|
1457
1509
|
|
|
1458
|
-
|
|
1510
|
+
const percentage = totalExecutableLines === 0
|
|
1511
|
+
? 0
|
|
1512
|
+
: Math.floor((totalCoveredLines / totalExecutableLines) * 10_000) / 100
|
|
1513
|
+
return { percentage, executableFiles }
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
function getTestCoverageLinesPercentage (coverage, skippedCoverage, rootDir) {
|
|
1517
|
+
return getTestCoverageLinesData(coverage, skippedCoverage, rootDir).percentage
|
|
1459
1518
|
}
|
|
1460
1519
|
|
|
1461
1520
|
function isLineCoveredByBitmap (bitmap, line) {
|
|
@@ -1552,19 +1611,77 @@ function fromCoverageMapToCoverage (coverageMap) {
|
|
|
1552
1611
|
}, {})
|
|
1553
1612
|
}
|
|
1554
1613
|
|
|
1614
|
+
/**
|
|
1615
|
+
* @param {number} code
|
|
1616
|
+
* @returns {boolean}
|
|
1617
|
+
*/
|
|
1618
|
+
function isAsciiDigit (code) {
|
|
1619
|
+
return code >= 0x30 && code <= 0x39
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
/**
|
|
1623
|
+
* Parse a V8 stack frame into its file path and 1-based line number.
|
|
1624
|
+
*
|
|
1625
|
+
* Stack text includes caller-controlled function names and can be overwritten. Scan numeric suffixes
|
|
1626
|
+
* right-to-left and discard V8's trailing anonymous location for eval frames.
|
|
1627
|
+
* @param {string} frame
|
|
1628
|
+
* @param {string} expectedPath A known path contained in the frame.
|
|
1629
|
+
* @returns {{ file: string, line: number } | null}
|
|
1630
|
+
*/
|
|
1631
|
+
function parseStackFrameLocation (frame, expectedPath) {
|
|
1632
|
+
let pathStart = frame.lastIndexOf(expectedPath)
|
|
1633
|
+
while (frame.charCodeAt(pathStart - 1) !== 0x28 /* ( */ &&
|
|
1634
|
+
!frame.startsWith('at ', pathStart - 3) &&
|
|
1635
|
+
!frame.startsWith('file://', pathStart - 7)) {
|
|
1636
|
+
const previousPathStart = frame.lastIndexOf(expectedPath, pathStart - 1)
|
|
1637
|
+
if (previousPathStart === -1) break
|
|
1638
|
+
pathStart = previousPathStart
|
|
1639
|
+
}
|
|
1640
|
+
const scope = frame.slice(pathStart)
|
|
1641
|
+
let end = scope.length
|
|
1642
|
+
if (scope.charCodeAt(end - 1) === 0x29 /* ) */) end--
|
|
1643
|
+
|
|
1644
|
+
while (true) {
|
|
1645
|
+
let lastNumberStart = end
|
|
1646
|
+
while (lastNumberStart > 0 && isAsciiDigit(scope.charCodeAt(lastNumberStart - 1))) {
|
|
1647
|
+
lastNumberStart--
|
|
1648
|
+
}
|
|
1649
|
+
if (lastNumberStart === end || scope.charCodeAt(lastNumberStart - 1) !== 0x3A /* : */) {
|
|
1650
|
+
return null
|
|
1651
|
+
}
|
|
1652
|
+
const colonBeforeLast = lastNumberStart - 1
|
|
1653
|
+
let lineStart = colonBeforeLast
|
|
1654
|
+
while (lineStart > 0 && isAsciiDigit(scope.charCodeAt(lineStart - 1))) {
|
|
1655
|
+
lineStart--
|
|
1656
|
+
}
|
|
1657
|
+
if (lineStart < colonBeforeLast && scope.charCodeAt(lineStart - 1) === 0x3A /* : */) {
|
|
1658
|
+
const fileEnd = lineStart - 1
|
|
1659
|
+
const anonymousStart = fileEnd - 11
|
|
1660
|
+
if (scope.charCodeAt(fileEnd - 1) === 0x3E /* > */ && anonymousStart >= 3 &&
|
|
1661
|
+
scope.startsWith('<anonymous>', anonymousStart) &&
|
|
1662
|
+
scope.charCodeAt(anonymousStart - 1) === 0x20 /* space */ &&
|
|
1663
|
+
scope.charCodeAt(anonymousStart - 2) === 0x2C /* , */ &&
|
|
1664
|
+
scope.charCodeAt(anonymousStart - 3) === 0x29 /* ) */) {
|
|
1665
|
+
end = anonymousStart - 3
|
|
1666
|
+
while (scope.charCodeAt(end - 1) === 0x29 /* ) */) end--
|
|
1667
|
+
continue
|
|
1668
|
+
}
|
|
1669
|
+
return { file: scope.slice(0, fileEnd), line: Number(scope.slice(lineStart, colonBeforeLast)) }
|
|
1670
|
+
}
|
|
1671
|
+
return { file: scope.slice(0, colonBeforeLast), line: Number(scope.slice(lastNumberStart, end)) }
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1555
1675
|
// Get the start line of a test by inspecting a given error's stack trace
|
|
1556
1676
|
function getTestLineStart (err, testSuitePath) {
|
|
1557
1677
|
if (!err.stack) {
|
|
1558
1678
|
return null
|
|
1559
1679
|
}
|
|
1560
|
-
// From https://github.com/felixge/node-stack-trace/blob/ba06dcdb50d465cd440d84a563836e293b360427/index.js#L40
|
|
1561
1680
|
const testFileLine = err.stack.split('\n').find(line => line.includes(testSuitePath))
|
|
1562
|
-
|
|
1563
|
-
const testFileLineMatch = testFileLine.match(/at (?:(.+?)\s+\()?(?:(.+?):(\d+)(?::(\d+))?|([^)]+))\)?/)
|
|
1564
|
-
return Number.parseInt(testFileLineMatch[3], 10) || null
|
|
1565
|
-
} catch {
|
|
1681
|
+
if (!testFileLine) {
|
|
1566
1682
|
return null
|
|
1567
1683
|
}
|
|
1684
|
+
return parseStackFrameLocation(testFileLine, testSuitePath)?.line || null
|
|
1568
1685
|
}
|
|
1569
1686
|
|
|
1570
1687
|
// Get the end line of a test by inspecting a given function's source code
|
|
@@ -1587,7 +1704,7 @@ function parseAnnotations (annotations) {
|
|
|
1587
1704
|
}
|
|
1588
1705
|
const { type, description } = annotation
|
|
1589
1706
|
if (type.startsWith('DD_TAGS')) {
|
|
1590
|
-
const regex =
|
|
1707
|
+
const regex = /^DD_TAGS\[([^\]]*)\]/
|
|
1591
1708
|
const match = regex.exec(type)
|
|
1592
1709
|
let tagValue = ''
|
|
1593
1710
|
if (match) {
|
|
@@ -1691,15 +1808,9 @@ function getFileAndLineNumberFromError (error, repositoryRoot) {
|
|
|
1691
1808
|
}
|
|
1692
1809
|
|
|
1693
1810
|
const topFrame = frames[topRelevantFrameIndex]
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
if (match) {
|
|
1699
|
-
const filePath = match[1]
|
|
1700
|
-
const lineNumber = Number(match[2])
|
|
1701
|
-
|
|
1702
|
-
return [filePath, lineNumber, topRelevantFrameIndex]
|
|
1811
|
+
const location = parseStackFrameLocation(topFrame, repositoryRoot)
|
|
1812
|
+
if (location) {
|
|
1813
|
+
return [location.file, location.line, topRelevantFrameIndex]
|
|
1703
1814
|
}
|
|
1704
1815
|
return []
|
|
1705
1816
|
}
|
|
@@ -2029,12 +2140,17 @@ function formatTestOptimizationList (items) {
|
|
|
2029
2140
|
const more = items.length - shown.length
|
|
2030
2141
|
const moreSuffix = more > 0 ? `\n ... and ${more} more` : ''
|
|
2031
2142
|
|
|
2032
|
-
|
|
2033
|
-
|
|
2143
|
+
let formattedItems = ''
|
|
2144
|
+
let isFirstItem = true
|
|
2145
|
+
for (const { text, suffix } of shown) {
|
|
2146
|
+
if (!isFirstItem) formattedItems += '\n'
|
|
2147
|
+
formattedItems += ` • ${truncateTestOptimizationNameMiddle(
|
|
2034
2148
|
sanitizeTestOptimizationName(text),
|
|
2035
2149
|
MAX_TEST_OPTIMIZATION_NAME_LENGTH
|
|
2036
|
-
)}${suffix ? ` (${suffix})` : ''}`
|
|
2037
|
-
|
|
2150
|
+
)}${suffix ? ` (${suffix})` : ''}`
|
|
2151
|
+
isFirstItem = false
|
|
2152
|
+
}
|
|
2153
|
+
return formattedItems + moreSuffix
|
|
2038
2154
|
}
|
|
2039
2155
|
|
|
2040
2156
|
/**
|
|
@@ -14,7 +14,7 @@ const INT_SEGMENT = /^[1-9][0-9]+$/ // Integer of size at least 2 (>=10)
|
|
|
14
14
|
const INT_ID_SEGMENT = /^(?=.*[0-9])[0-9._-]{3,}$/ // Mixed string with digits and delimiters
|
|
15
15
|
const HEX_SEGMENT = /^(?=.*[0-9])[A-Fa-f0-9]{6,}$/ // Hexadecimal digits of size at least 6 with at least one decimal digit
|
|
16
16
|
const HEX_ID_SEGMENT = /^(?=.*[0-9])[A-Fa-f0-9._-]{6,}$/ // Mixed string with hex digits and delimiters
|
|
17
|
-
const
|
|
17
|
+
const SPECIAL_CHARACTER_SEGMENT = /[%&'()*+,:=@]/
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Extract full URL from HTTP request
|
|
@@ -167,7 +167,7 @@ function calculateHttpEndpoint (url) {
|
|
|
167
167
|
|
|
168
168
|
if (HEX_ID_SEGMENT.test(element)) return '{param:hex_id}'
|
|
169
169
|
|
|
170
|
-
if (
|
|
170
|
+
if (element.length >= 20 || SPECIAL_CHARACTER_SEGMENT.test(element)) return '{param:str}'
|
|
171
171
|
|
|
172
172
|
// No match
|
|
173
173
|
return element
|