dd-trace 5.108.0 → 5.110.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/ci/init.js +4 -3
- package/index.d.ts +88 -25
- package/loader-hook.mjs +88 -2
- package/package.json +19 -14
- package/packages/datadog-esbuild/index.js +26 -0
- package/packages/datadog-esbuild/src/utils.js +46 -3
- package/packages/datadog-instrumentations/src/ai.js +43 -48
- package/packages/datadog-instrumentations/src/aws-durable-execution-sdk-js-context-methods.js +18 -0
- package/packages/datadog-instrumentations/src/aws-durable-execution-sdk-js.js +111 -0
- package/packages/datadog-instrumentations/src/aws-sdk.js +3 -1
- package/packages/datadog-instrumentations/src/connect.js +4 -3
- package/packages/datadog-instrumentations/src/electron.js +1 -1
- package/packages/datadog-instrumentations/src/fastify.js +4 -12
- package/packages/datadog-instrumentations/src/graphql.js +17 -6
- package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +81 -8
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/instrument.js +27 -2
- package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +65 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +6 -4
- package/packages/datadog-instrumentations/src/helpers/require-optional-peer.js +17 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/aws-durable-execution-sdk-js.js +31 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +18 -2
- package/packages/datadog-instrumentations/src/http/client.js +12 -2
- package/packages/datadog-instrumentations/src/ioredis.js +0 -1
- package/packages/datadog-instrumentations/src/iovalkey.js +1 -2
- package/packages/datadog-instrumentations/src/koa.js +3 -2
- package/packages/datadog-instrumentations/src/mariadb.js +4 -2
- package/packages/datadog-instrumentations/src/mocha/main.js +3 -3
- package/packages/datadog-instrumentations/src/next.js +44 -10
- package/packages/datadog-instrumentations/src/openai.js +77 -18
- package/packages/datadog-instrumentations/src/otel-sdk-trace.js +7 -6
- package/packages/datadog-instrumentations/src/playwright.js +1 -1
- package/packages/datadog-instrumentations/src/redis.js +0 -1
- package/packages/datadog-instrumentations/src/restify.js +4 -3
- package/packages/datadog-instrumentations/src/router.js +18 -5
- package/packages/datadog-instrumentations/src/selenium.js +1 -2
- package/packages/datadog-instrumentations/src/tedious.js +28 -0
- package/packages/datadog-instrumentations/src/vitest.js +323 -7
- package/packages/datadog-plugin-avsc/src/schema_iterator.js +2 -2
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/checkpoint.js +31 -0
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/client.js +55 -0
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/context.js +114 -0
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/handler.js +128 -0
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/index.js +19 -0
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/trace-checkpoint.js +224 -0
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +43 -0
- package/packages/datadog-plugin-aws-sdk/src/base.js +14 -16
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +43 -6
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +100 -37
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +108 -99
- package/packages/datadog-plugin-bullmq/src/filter.js +35 -0
- package/packages/datadog-plugin-bullmq/src/producer.js +84 -4
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +9 -1
- package/packages/datadog-plugin-fs/src/index.js +1 -0
- package/packages/datadog-plugin-http/src/client.js +8 -3
- package/packages/datadog-plugin-http2/src/client.js +5 -2
- package/packages/datadog-plugin-mysql/src/index.js +7 -2
- package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +2 -2
- package/packages/datadog-plugin-redis/src/index.js +1 -2
- package/packages/datadog-plugin-undici/src/index.js +5 -2
- package/packages/datadog-plugin-vitest/src/index.js +11 -2
- package/packages/datadog-webpack/index.js +17 -1
- package/packages/datadog-webpack/src/optional-peer-loader.js +17 -0
- package/packages/dd-trace/src/aiguard/channels.js +0 -1
- package/packages/dd-trace/src/aiguard/index.js +11 -49
- package/packages/dd-trace/src/aiguard/integrations/evaluate.js +46 -0
- package/packages/dd-trace/src/aiguard/integrations/openai.js +66 -0
- package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +78 -0
- package/packages/{datadog-instrumentations/src/helpers/ai-messages.js → dd-trace/src/aiguard/messages/openai.js} +85 -193
- package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +185 -0
- package/packages/dd-trace/src/aiguard/sdk.js +13 -1
- package/packages/dd-trace/src/appsec/api_security/index.js +55 -0
- package/packages/dd-trace/src/appsec/api_security/sampler.js +147 -0
- package/packages/dd-trace/src/appsec/channels.js +1 -0
- package/packages/dd-trace/src/appsec/downstream_requests.js +111 -58
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/ldap-sensitive-analyzer.js +54 -12
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +5 -1
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +29 -4
- package/packages/dd-trace/src/appsec/index.js +10 -6
- package/packages/dd-trace/src/appsec/rasp/ssrf.js +19 -11
- package/packages/dd-trace/src/appsec/reporter.js +1 -0
- package/packages/dd-trace/src/appsec/telemetry/api_security.js +34 -0
- package/packages/dd-trace/src/appsec/telemetry/index.js +26 -0
- package/packages/dd-trace/src/ci-visibility/requests/request.js +1 -17
- package/packages/dd-trace/src/config/defaults.js +22 -3
- package/packages/dd-trace/src/config/generated-config-types.d.ts +538 -1
- package/packages/dd-trace/src/config/helper.js +133 -33
- package/packages/dd-trace/src/config/index.js +4 -1
- package/packages/dd-trace/src/config/parsers.js +19 -1
- package/packages/dd-trace/src/config/supported-configurations.json +45 -5
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +49 -35
- package/packages/dd-trace/src/debugger/devtools_client/index.js +31 -66
- package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +83 -0
- package/packages/dd-trace/src/debugger/devtools_client/state.js +1 -0
- package/packages/dd-trace/src/debugger/index.js +5 -0
- package/packages/dd-trace/src/debugger/probe_sampler.js +112 -0
- package/packages/dd-trace/src/debugger/probe_sampler_constants.js +19 -0
- package/packages/dd-trace/src/dogstatsd.js +15 -8
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +77 -26
- package/packages/dd-trace/src/exporters/agentless/index.js +7 -5
- package/packages/dd-trace/src/exporters/agentless/intake.js +43 -0
- package/packages/dd-trace/src/exporters/agentless/writer.js +5 -4
- package/packages/dd-trace/src/exporters/common/request.js +1 -20
- package/packages/dd-trace/src/exporters/common/url.js +34 -0
- package/packages/dd-trace/src/exporters/span-stats/writer.js +1 -3
- package/packages/dd-trace/src/guardrails/index.js +8 -5
- package/packages/dd-trace/src/index.js +6 -4
- package/packages/dd-trace/src/llmobs/constants/tags.js +6 -0
- package/packages/dd-trace/src/llmobs/index.js +16 -5
- package/packages/dd-trace/src/llmobs/plugins/genai/util.js +3 -3
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +5 -1
- package/packages/dd-trace/src/llmobs/sdk.js +5 -3
- package/packages/dd-trace/src/llmobs/span_processor.js +4 -0
- package/packages/dd-trace/src/llmobs/tagger.js +55 -0
- package/packages/dd-trace/src/llmobs/telemetry.js +1 -0
- package/packages/dd-trace/src/log/index.js +18 -20
- package/packages/dd-trace/src/openfeature/flagging_provider.js +3 -1
- package/packages/dd-trace/src/opentelemetry/bridge-span-base.js +5 -4
- package/packages/dd-trace/src/opentelemetry/metrics/instruments.js +2 -1
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +4 -3
- package/packages/dd-trace/src/opentelemetry/metrics/time.js +19 -0
- package/packages/dd-trace/src/opentelemetry/otlp/common.proto +2 -2
- package/packages/dd-trace/src/opentelemetry/otlp/logs.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/otlp/logs_service.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/otlp/metrics.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/otlp/metrics_service.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +11 -4
- package/packages/dd-trace/src/opentelemetry/span-helpers.js +29 -13
- package/packages/dd-trace/src/opentelemetry/span.js +14 -3
- package/packages/dd-trace/src/opentelemetry/span_context.js +1 -0
- package/packages/dd-trace/src/opentelemetry/trace/index.js +2 -1
- package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +3 -2
- package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +26 -13
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +3 -3
- package/packages/dd-trace/src/opentracing/span_context.js +17 -0
- package/packages/dd-trace/src/plugin_manager.js +7 -1
- package/packages/dd-trace/src/plugins/ci_plugin.js +36 -3
- package/packages/dd-trace/src/plugins/index.js +3 -0
- package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +276 -0
- package/packages/dd-trace/src/plugins/util/test.js +80 -0
- package/packages/dd-trace/src/plugins/util/url.js +68 -1
- package/packages/dd-trace/src/plugins/util/web.js +19 -36
- package/packages/dd-trace/src/priority_sampler.js +1 -13
- package/packages/dd-trace/src/profiling/config.js +129 -198
- package/packages/dd-trace/src/profiling/constants.js +4 -7
- package/packages/dd-trace/src/profiling/exporter_cli.js +10 -10
- package/packages/dd-trace/src/profiling/exporters/agent.js +23 -16
- package/packages/dd-trace/src/profiling/exporters/event_serializer.js +45 -14
- package/packages/dd-trace/src/profiling/exporters/file.js +16 -5
- package/packages/dd-trace/src/profiling/oom.js +71 -0
- package/packages/dd-trace/src/profiling/profiler.js +35 -23
- package/packages/dd-trace/src/profiling/profilers/events.js +14 -7
- package/packages/dd-trace/src/profiling/profilers/space.js +27 -18
- package/packages/dd-trace/src/profiling/profilers/wall.js +35 -25
- package/packages/dd-trace/src/ritm.js +4 -2
- package/packages/dd-trace/src/serverless.js +1 -2
- package/packages/dd-trace/src/service-naming/schemas/util.js +36 -5
- package/packages/dd-trace/src/service-naming/schemas/v0/serverless.js +12 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/serverless.js +12 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/web.js +5 -3
- package/packages/dd-trace/src/span_format.js +2 -1
- package/packages/dd-trace/src/span_processor.js +6 -0
- package/packages/dd-trace/src/telemetry/send-data.js +12 -10
- package/packages/dd-trace/src/tracer.js +15 -1
- package/packages/dd-trace/src/util.js +14 -0
- package/register.js +58 -1
- package/vendor/dist/@apm-js-collab/code-transformer/index.js +1 -1
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/pprof-format/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/helpers/openai-ai-guard.js +0 -284
- package/packages/dd-trace/src/appsec/api_security_sampler.js +0 -117
|
@@ -8,6 +8,7 @@ const tags = require('../../../ext/tags')
|
|
|
8
8
|
const formats = require('../../../ext/formats')
|
|
9
9
|
const HTTP_HEADERS = formats.HTTP_HEADERS
|
|
10
10
|
const urlFilter = require('../../dd-trace/src/plugins/util/urlfilter')
|
|
11
|
+
const { buildClientHttpUrl } = require('../../dd-trace/src/plugins/util/url')
|
|
11
12
|
const log = require('../../dd-trace/src/log')
|
|
12
13
|
const { CLIENT_PORT_KEY, COMPONENT, ERROR_MESSAGE, ERROR_TYPE, ERROR_STACK } = require('../../dd-trace/src/constants')
|
|
13
14
|
|
|
@@ -27,13 +28,17 @@ class HttpClientPlugin extends ClientPlugin {
|
|
|
27
28
|
const protocol = options.protocol || agent.protocol || 'http:'
|
|
28
29
|
const hostname = options.hostname || options.host || 'localhost'
|
|
29
30
|
const host = options.port ? `${hostname}:${options.port}` : hostname
|
|
30
|
-
const
|
|
31
|
+
const base = `${protocol}//${host}`
|
|
32
|
+
// A URL object (e.g. from the fetch integration) carries the query in
|
|
33
|
+
// `options.search`, not `options.path`; keep it so url.full retains the query.
|
|
34
|
+
const pathname = options.path || `${options.pathname || ''}${options.search || ''}`
|
|
31
35
|
const path = pathname ? pathname.split(/[?#]/)[0] : '/'
|
|
32
|
-
const uri = `${
|
|
36
|
+
const uri = `${base}${path}`
|
|
33
37
|
|
|
34
38
|
const allowed = this.config.filter(uri)
|
|
35
39
|
|
|
36
40
|
const method = (options.method || 'GET').toUpperCase()
|
|
41
|
+
const otelSemantics = this.config.DD_TRACE_OTEL_SEMANTICS_ENABLED
|
|
37
42
|
const childOf = store && allowed ? store.span : null
|
|
38
43
|
// TODO delegate to super.startspan
|
|
39
44
|
const span = this.startSpan(this.operationName(), {
|
|
@@ -46,7 +51,7 @@ class HttpClientPlugin extends ClientPlugin {
|
|
|
46
51
|
'resource.name': method,
|
|
47
52
|
'span.type': 'http',
|
|
48
53
|
'http.method': method,
|
|
49
|
-
'http.url': uri,
|
|
54
|
+
'http.url': otelSemantics ? buildClientHttpUrl(this.config, base, pathname, uri) : uri,
|
|
50
55
|
'out.host': hostname,
|
|
51
56
|
},
|
|
52
57
|
metrics: {
|
|
@@ -10,6 +10,7 @@ const kinds = require('../../../ext/kinds')
|
|
|
10
10
|
const formats = require('../../../ext/formats')
|
|
11
11
|
const { COMPONENT, CLIENT_PORT_KEY } = require('../../dd-trace/src/constants')
|
|
12
12
|
const urlFilter = require('../../dd-trace/src/plugins/util/urlfilter')
|
|
13
|
+
const { buildClientHttpUrl } = require('../../dd-trace/src/plugins/util/url')
|
|
13
14
|
|
|
14
15
|
const HTTP_HEADERS = formats.HTTP_HEADERS
|
|
15
16
|
const HTTP_STATUS_CODE = tags.HTTP_STATUS_CODE
|
|
@@ -33,8 +34,10 @@ class Http2ClientPlugin extends ClientPlugin {
|
|
|
33
34
|
const path = headers[HTTP2_HEADER_PATH] || '/'
|
|
34
35
|
const pathname = path.split(/[?#]/)[0]
|
|
35
36
|
const method = headers[HTTP2_HEADER_METHOD] || HTTP2_METHOD_GET
|
|
36
|
-
const
|
|
37
|
+
const base = `${sessionDetails.protocol}//${sessionDetails.host}:${sessionDetails.port}`
|
|
38
|
+
const uri = `${base}${pathname}`
|
|
37
39
|
const allowed = this.config.filter(uri)
|
|
40
|
+
const otelSemantics = this.config.DD_TRACE_OTEL_SEMANTICS_ENABLED
|
|
38
41
|
|
|
39
42
|
const store = storage('legacy').getStore()
|
|
40
43
|
const childOf = store && allowed ? store.span : null
|
|
@@ -48,7 +51,7 @@ class Http2ClientPlugin extends ClientPlugin {
|
|
|
48
51
|
'resource.name': method,
|
|
49
52
|
'span.type': 'http',
|
|
50
53
|
'http.method': method,
|
|
51
|
-
'http.url': uri,
|
|
54
|
+
'http.url': otelSemantics ? buildClientHttpUrl(this.config, base, path, uri) : uri,
|
|
52
55
|
'out.host': sessionDetails.host,
|
|
53
56
|
},
|
|
54
57
|
metrics: {
|
|
@@ -11,11 +11,16 @@ class MySQLPlugin extends DatabasePlugin {
|
|
|
11
11
|
constructor () {
|
|
12
12
|
super(...arguments)
|
|
13
13
|
|
|
14
|
+
// Capture into `currentStore` (not `parentStore`) so connection:finish can
|
|
15
|
+
// restore the caller context even when the connection resolves inside an
|
|
16
|
+
// instrumentation skip (a noop store), as the mariadb pool does: the store
|
|
17
|
+
// binding only honors an explicit `currentStore` through a noop store.
|
|
18
|
+
// Without a skip (mysql/mysql2) this is unchanged.
|
|
14
19
|
this.addSub(`apm:${this.component}:connection:start`, ctx => {
|
|
15
|
-
ctx.
|
|
20
|
+
ctx.currentStore = storage('legacy').getStore()
|
|
16
21
|
})
|
|
17
22
|
|
|
18
|
-
this.addBind(`apm:${this.component}:connection:finish`, ctx => ctx.
|
|
23
|
+
this.addBind(`apm:${this.component}:connection:finish`, ctx => ctx.currentStore)
|
|
19
24
|
}
|
|
20
25
|
|
|
21
26
|
bindStart (ctx) {
|
|
@@ -47,10 +47,10 @@ class SchemaExtractor {
|
|
|
47
47
|
|
|
48
48
|
static extractProperty (field, schemaName, fieldName, builder, depth) {
|
|
49
49
|
let array = false
|
|
50
|
-
let description
|
|
51
50
|
let ref
|
|
52
51
|
let enumValues
|
|
53
52
|
|
|
53
|
+
const description = field.comment
|
|
54
54
|
const resolvedType = field.resolvedType ? field.resolvedType.constructor.name : field.type
|
|
55
55
|
|
|
56
56
|
const isRepeatedField = field.rule === 'repeated'
|
|
@@ -124,7 +124,7 @@ class SchemaExtractor {
|
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
iterateOverSchema (builder) {
|
|
127
|
-
|
|
127
|
+
SchemaExtractor.extractSchema(this.schema, builder, 0)
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
static attachSchemaOnSpan (args, span, operation, tracer) {
|
|
@@ -28,7 +28,7 @@ class RedisPlugin extends CachePlugin {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
bindStart (ctx) {
|
|
31
|
-
const {
|
|
31
|
+
const { command, args, argsStartIndex, connectionOptions, connectionName } = ctx
|
|
32
32
|
|
|
33
33
|
const resource = command
|
|
34
34
|
const normalizedCommand = command.toUpperCase()
|
|
@@ -55,7 +55,6 @@ class RedisPlugin extends CachePlugin {
|
|
|
55
55
|
type: this._spanType,
|
|
56
56
|
meta: {
|
|
57
57
|
'db.type': this._spanType,
|
|
58
|
-
'db.name': db || '0',
|
|
59
58
|
[this.#rawCommandKey]: formatCommand(normalizedCommand, args, argsStartIndex),
|
|
60
59
|
'out.host': connectionOptions.host,
|
|
61
60
|
[CLIENT_PORT_KEY]: connectionOptions.port,
|
|
@@ -6,6 +6,7 @@ const tags = require('../../../ext/tags')
|
|
|
6
6
|
const formats = require('../../../ext/formats')
|
|
7
7
|
const HTTP_HEADERS = formats.HTTP_HEADERS
|
|
8
8
|
const log = require('../../dd-trace/src/log')
|
|
9
|
+
const { buildClientHttpUrl } = require('../../dd-trace/src/plugins/util/url')
|
|
9
10
|
const { CLIENT_PORT_KEY } = require('../../dd-trace/src/constants')
|
|
10
11
|
|
|
11
12
|
const {
|
|
@@ -60,10 +61,12 @@ class UndiciPlugin extends HttpClientPlugin {
|
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
const host = port ? `${hostname}:${port}` : hostname
|
|
64
|
+
const base = `${protocol}//${host}`
|
|
63
65
|
const pathname = path.split(/[?#]/)[0]
|
|
64
|
-
const uri = `${
|
|
66
|
+
const uri = `${base}${pathname}`
|
|
65
67
|
|
|
66
68
|
const allowed = this.config.filter(uri)
|
|
69
|
+
const otelSemantics = this.config.DD_TRACE_OTEL_SEMANTICS_ENABLED
|
|
67
70
|
const childOf = store && allowed ? store.span : null
|
|
68
71
|
|
|
69
72
|
const span = this.startSpan(this.operationName(), {
|
|
@@ -71,7 +74,7 @@ class UndiciPlugin extends HttpClientPlugin {
|
|
|
71
74
|
meta: {
|
|
72
75
|
'span.kind': 'client',
|
|
73
76
|
'http.method': method,
|
|
74
|
-
'http.url': uri,
|
|
77
|
+
'http.url': otelSemantics ? buildClientHttpUrl(this.config, base, path, uri) : uri,
|
|
75
78
|
'out.host': hostname,
|
|
76
79
|
},
|
|
77
80
|
metrics: {
|
|
@@ -9,6 +9,7 @@ const {
|
|
|
9
9
|
finishAllTraceSpans,
|
|
10
10
|
getTestSuitePath,
|
|
11
11
|
getTestSuiteCommonTags,
|
|
12
|
+
getTestLevelsMetadataTags,
|
|
12
13
|
getTestSessionName,
|
|
13
14
|
getIsFaultyEarlyFlakeDetection,
|
|
14
15
|
TEST_SOURCE_FILE,
|
|
@@ -16,6 +17,7 @@ const {
|
|
|
16
17
|
TEST_CODE_COVERAGE_LINES_PCT,
|
|
17
18
|
TEST_CODE_OWNERS,
|
|
18
19
|
TEST_COMMAND,
|
|
20
|
+
TEST_LEVELS_METADATA,
|
|
19
21
|
TEST_SESSION_NAME,
|
|
20
22
|
TEST_SOURCE_START,
|
|
21
23
|
TEST_IS_NEW,
|
|
@@ -63,6 +65,8 @@ class VitestPlugin extends CiPlugin {
|
|
|
63
65
|
testSessionId: testSessionSpanContext?.toTraceId(),
|
|
64
66
|
testModuleId: testModuleSpanContext?.toSpanId(),
|
|
65
67
|
testCommand: this.command,
|
|
68
|
+
repositoryRoot: this.repositoryRoot,
|
|
69
|
+
codeOwnersEntries: this.codeOwnersEntries,
|
|
66
70
|
})
|
|
67
71
|
})
|
|
68
72
|
|
|
@@ -307,10 +311,11 @@ class VitestPlugin extends CiPlugin {
|
|
|
307
311
|
})
|
|
308
312
|
|
|
309
313
|
this.addBind('ci:vitest:test-suite:start', (ctx) => {
|
|
310
|
-
const { testSuiteAbsolutePath, frameworkVersion } = ctx
|
|
314
|
+
const { codeOwnersEntries, repositoryRoot, testSuiteAbsolutePath, frameworkVersion } = ctx
|
|
311
315
|
|
|
312
316
|
const testCommand = ctx.testCommand || 'vitest run'
|
|
313
317
|
const { testSessionId, testModuleId } = ctx
|
|
318
|
+
this._setRepositoryRoot(repositoryRoot, codeOwnersEntries)
|
|
314
319
|
this.command = testCommand
|
|
315
320
|
this.frameworkVersion = frameworkVersion
|
|
316
321
|
const testSessionSpanContext = testSessionId && testModuleId
|
|
@@ -325,7 +330,11 @@ class VitestPlugin extends CiPlugin {
|
|
|
325
330
|
const testSessionName = getTestSessionName(this.config, trimmedCommand, this.testEnvironmentMetadata)
|
|
326
331
|
if (this.tracer._exporter.addMetadataTags) {
|
|
327
332
|
this.tracer._exporter.addMetadataTags({
|
|
328
|
-
|
|
333
|
+
[TEST_LEVELS_METADATA]: {
|
|
334
|
+
[TEST_COMMAND]: testCommand,
|
|
335
|
+
[TEST_SESSION_NAME]: testSessionName,
|
|
336
|
+
...getTestLevelsMetadataTags(this.testEnvironmentMetadata),
|
|
337
|
+
},
|
|
329
338
|
test: getLibraryCapabilitiesTags(this.constructor.id),
|
|
330
339
|
})
|
|
331
340
|
}
|
|
@@ -6,6 +6,7 @@ const fs = require('node:fs')
|
|
|
6
6
|
const instrumentations = require('../datadog-instrumentations/src/helpers/instrumentations')
|
|
7
7
|
const extractPackageAndModulePath = require('../datadog-instrumentations/src/helpers/extract-package-and-module-path')
|
|
8
8
|
const hooks = require('../datadog-instrumentations/src/helpers/hooks')
|
|
9
|
+
const { matchesOptionalPeerFile } = require('../datadog-instrumentations/src/helpers/optional-peer-bundler')
|
|
9
10
|
const { isESMFile } = require('../datadog-esbuild/src/utils')
|
|
10
11
|
const log = require('./src/log')
|
|
11
12
|
|
|
@@ -130,11 +131,26 @@ class DatadogWebpackPlugin {
|
|
|
130
131
|
nmf.hooks.afterResolve.tap(PLUGIN_NAME, (resolveData) => {
|
|
131
132
|
const { createData } = resolveData
|
|
132
133
|
const resource = createData?.resource
|
|
133
|
-
if (!resource
|
|
134
|
+
if (!resource) {
|
|
134
135
|
return
|
|
135
136
|
}
|
|
136
137
|
|
|
137
138
|
const normalizedResource = resource.replaceAll('\\', '/')
|
|
139
|
+
|
|
140
|
+
// Rewrite optional-peer loads so installed peers get bundled and survive relocation
|
|
141
|
+
// (#8980); absent peers stay opaque, so a build that does not opt into the feature does
|
|
142
|
+
// not follow their dependency chain (#8635).
|
|
143
|
+
if (matchesOptionalPeerFile(normalizedResource)) {
|
|
144
|
+
createData.loaders = createData.loaders || []
|
|
145
|
+
createData.loaders.push({ loader: require.resolve('./src/optional-peer-loader') })
|
|
146
|
+
log.debug('INLINE: optional-peer loader applied to %s', normalizedResource)
|
|
147
|
+
return
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (!resource.includes('node_modules')) {
|
|
151
|
+
return
|
|
152
|
+
}
|
|
153
|
+
|
|
138
154
|
const { pkg, path: modulePath, pkgJson } = extractPackageAndModulePath(normalizedResource)
|
|
139
155
|
if (!pkg) {
|
|
140
156
|
return
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { rewriteOptionalPeerLoads } = require('../../datadog-instrumentations/src/helpers/optional-peer-bundler')
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Webpack loader applied to the optional-peer loader files. Rewrites each
|
|
7
|
+
* `requireOptionalPeer('name')` whose peer is installed at build time into a literal
|
|
8
|
+
* `require('name')` so webpack bundles the peer (#8980). Peers that are absent stay opaque, so
|
|
9
|
+
* builds that do not opt into the feature keep the #8635 guarantee.
|
|
10
|
+
*
|
|
11
|
+
* @param {string} source
|
|
12
|
+
* @returns {string}
|
|
13
|
+
*/
|
|
14
|
+
module.exports = function optionalPeerLoader (source) {
|
|
15
|
+
this.cacheable(false)
|
|
16
|
+
return rewriteOptionalPeerLoads(source, this.context)
|
|
17
|
+
}
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const log = require('../log')
|
|
4
|
-
const { incomingHttpRequestStart
|
|
4
|
+
const { incomingHttpRequestStart } = require('./channels')
|
|
5
|
+
const openaiIntegration = require('./integrations/openai')
|
|
6
|
+
const vercelAiIntegration = require('./integrations/vercel-ai')
|
|
5
7
|
const AIGuard = require('./sdk')
|
|
6
|
-
const { SOURCE_AUTO, INTEGRATION_NONE } = require('./tags')
|
|
7
8
|
|
|
8
9
|
let isEnabled = false
|
|
9
10
|
let aiguard
|
|
10
11
|
let block
|
|
12
|
+
let disableOpenAIIntegration
|
|
13
|
+
let disableVercelAiIntegration
|
|
11
14
|
|
|
12
15
|
function onIncomingHttpRequestStart () {
|
|
13
16
|
// No-op: subscribing ensures the HTTP plugin spreads req onto the store
|
|
@@ -21,7 +24,8 @@ function enable (tracer, config) {
|
|
|
21
24
|
block = config.experimental?.aiguard?.block !== false
|
|
22
25
|
|
|
23
26
|
incomingHttpRequestStart.subscribe(onIncomingHttpRequestStart)
|
|
24
|
-
|
|
27
|
+
disableOpenAIIntegration = openaiIntegration.enable(aiguard, block)
|
|
28
|
+
disableVercelAiIntegration = vercelAiIntegration.enable(aiguard, block)
|
|
25
29
|
|
|
26
30
|
isEnabled = true
|
|
27
31
|
} catch (err) {
|
|
@@ -34,56 +38,14 @@ function disable () {
|
|
|
34
38
|
if (!isEnabled) return
|
|
35
39
|
|
|
36
40
|
incomingHttpRequestStart.unsubscribe(onIncomingHttpRequestStart)
|
|
37
|
-
|
|
41
|
+
disableOpenAIIntegration?.()
|
|
42
|
+
disableVercelAiIntegration?.()
|
|
38
43
|
|
|
39
44
|
aiguard = undefined
|
|
40
45
|
isEnabled = false
|
|
41
46
|
block = false
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Handles channel messages with pre-converted messages.
|
|
46
|
-
*
|
|
47
|
-
* @param {object} ctx
|
|
48
|
-
* @param {Array<object>} ctx.messages
|
|
49
|
-
* @param {string} [ctx.integration]
|
|
50
|
-
* @param {object} [ctx.parentSpan] - LLM span to parent the `ai_guard` span under.
|
|
51
|
-
* @param {AbortController} ctx.abortController
|
|
52
|
-
* @param {Array<Promise<void>>} ctx.pending - Subscribers push only when they evaluate.
|
|
53
|
-
*/
|
|
54
|
-
function onEvaluate (ctx) {
|
|
55
|
-
// Decline to evaluate empty payloads by not pushing to pending.
|
|
56
|
-
if (!ctx.messages?.length) {
|
|
57
|
-
return
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const opts = {
|
|
61
|
-
block,
|
|
62
|
-
source: SOURCE_AUTO,
|
|
63
|
-
integration: ctx.integration || INTEGRATION_NONE,
|
|
64
|
-
childOf: ctx.parentSpan,
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
try {
|
|
68
|
-
ctx.pending.push(aiguard.evaluate(ctx.messages, opts).catch(handleEvaluationError.bind(null, ctx)))
|
|
69
|
-
} catch (err) {
|
|
70
|
-
ctx.pending.push(Promise.resolve().then(() => handleEvaluationError(ctx, err)))
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Handles an AI Guard evaluation failure.
|
|
76
|
-
*
|
|
77
|
-
* @param {object} ctx
|
|
78
|
-
* @param {AbortController} ctx.abortController
|
|
79
|
-
* @param {Error} err
|
|
80
|
-
*/
|
|
81
|
-
function handleEvaluationError (ctx, err) {
|
|
82
|
-
if (err.name === 'AIGuardAbortError') {
|
|
83
|
-
ctx.abortController.abort(err)
|
|
84
|
-
} else {
|
|
85
|
-
log.error('AIGuard: unexpected error during evaluation: %s', err.message)
|
|
86
|
-
}
|
|
47
|
+
disableOpenAIIntegration = undefined
|
|
48
|
+
disableVercelAiIntegration = undefined
|
|
87
49
|
}
|
|
88
50
|
|
|
89
51
|
module.exports = { enable, disable }
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const log = require('../../log')
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Starts one AI Guard evaluation for a lifecycle ctx.
|
|
7
|
+
*
|
|
8
|
+
* Async evaluations are pushed synchronously during channel publication. If
|
|
9
|
+
* evaluation throws synchronously, the error is handled before publish returns.
|
|
10
|
+
*
|
|
11
|
+
* @param {object} ctx
|
|
12
|
+
* @param {AbortController} ctx.abortController
|
|
13
|
+
* @param {object} [ctx.parentSpan]
|
|
14
|
+
* @param {Array<Promise<void>>} ctx.pending
|
|
15
|
+
* @param {object} aiguard
|
|
16
|
+
* @param {Array<object>|undefined} messages
|
|
17
|
+
* @param {object} opts
|
|
18
|
+
*/
|
|
19
|
+
function pushEvaluation (ctx, aiguard, messages, opts) {
|
|
20
|
+
if (!messages?.length) return
|
|
21
|
+
|
|
22
|
+
const evaluateOpts = ctx.parentSpan ? { ...opts, childOf: ctx.parentSpan } : opts
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
ctx.pending.push(aiguard.evaluate(messages, evaluateOpts).catch(handleEvaluationError.bind(null, ctx)))
|
|
26
|
+
} catch (err) {
|
|
27
|
+
handleEvaluationError(ctx, err)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Handles an AI Guard evaluation failure.
|
|
33
|
+
*
|
|
34
|
+
* @param {object} ctx
|
|
35
|
+
* @param {AbortController} ctx.abortController
|
|
36
|
+
* @param {Error} err
|
|
37
|
+
*/
|
|
38
|
+
function handleEvaluationError (ctx, err) {
|
|
39
|
+
if (err.name === 'AIGuardAbortError') {
|
|
40
|
+
ctx.abortController.abort(err)
|
|
41
|
+
} else {
|
|
42
|
+
log.error('AIGuard: unexpected error during evaluation: %s', err.message)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
module.exports = { pushEvaluation }
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { channel } = require('dc-polyfill')
|
|
4
|
+
|
|
5
|
+
const {
|
|
6
|
+
getChatCompletionsInputMessages,
|
|
7
|
+
getChatCompletionsOutputMessages,
|
|
8
|
+
getResponsesInputMessages,
|
|
9
|
+
getResponsesOutputMessages,
|
|
10
|
+
} = require('../messages/openai')
|
|
11
|
+
const { SOURCE_AUTO } = require('../tags')
|
|
12
|
+
const { pushEvaluation } = require('./evaluate')
|
|
13
|
+
|
|
14
|
+
const chatCompletionsBeforeChannel = channel('dd-trace:openai:chat.completions:before')
|
|
15
|
+
const chatCompletionsAfterChannel = channel('dd-trace:openai:chat.completions:after')
|
|
16
|
+
const responsesBeforeChannel = channel('dd-trace:openai:responses:before')
|
|
17
|
+
const responsesAfterChannel = channel('dd-trace:openai:responses:after')
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Subscribes AI Guard to OpenAI lifecycle channels.
|
|
21
|
+
*
|
|
22
|
+
* @param {object} aiguard
|
|
23
|
+
* @param {boolean} block
|
|
24
|
+
* @returns {() => void}
|
|
25
|
+
*/
|
|
26
|
+
function enable (aiguard, block) {
|
|
27
|
+
const opts = { block, source: SOURCE_AUTO, integration: 'openai' }
|
|
28
|
+
|
|
29
|
+
function onChatCompletionsBefore (ctx) {
|
|
30
|
+
pushEvaluation(ctx, aiguard, getChatCompletionsInputMessages(ctx.args?.[0]), opts)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function onChatCompletionsAfter (ctx) {
|
|
34
|
+
const inputMessages = getChatCompletionsInputMessages(ctx.args?.[0])
|
|
35
|
+
if (!inputMessages?.length) return
|
|
36
|
+
for (const message of getChatCompletionsOutputMessages(ctx.body)) {
|
|
37
|
+
pushEvaluation(ctx, aiguard, [...inputMessages, message], opts)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function onResponsesBefore (ctx) {
|
|
42
|
+
pushEvaluation(ctx, aiguard, getResponsesInputMessages(ctx.args?.[0]), opts)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function onResponsesAfter (ctx) {
|
|
46
|
+
const inputMessages = getResponsesInputMessages(ctx.args?.[0])
|
|
47
|
+
if (!inputMessages?.length) return
|
|
48
|
+
const outputMessages = getResponsesOutputMessages(ctx.body)
|
|
49
|
+
if (!outputMessages.length) return
|
|
50
|
+
pushEvaluation(ctx, aiguard, [...inputMessages, ...outputMessages], opts)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
chatCompletionsBeforeChannel.subscribe(onChatCompletionsBefore)
|
|
54
|
+
chatCompletionsAfterChannel.subscribe(onChatCompletionsAfter)
|
|
55
|
+
responsesBeforeChannel.subscribe(onResponsesBefore)
|
|
56
|
+
responsesAfterChannel.subscribe(onResponsesAfter)
|
|
57
|
+
|
|
58
|
+
return function disable () {
|
|
59
|
+
chatCompletionsBeforeChannel.unsubscribe(onChatCompletionsBefore)
|
|
60
|
+
chatCompletionsAfterChannel.unsubscribe(onChatCompletionsAfter)
|
|
61
|
+
responsesBeforeChannel.unsubscribe(onResponsesBefore)
|
|
62
|
+
responsesAfterChannel.unsubscribe(onResponsesAfter)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
module.exports = { enable }
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { channel } = require('dc-polyfill')
|
|
4
|
+
|
|
5
|
+
const { buildOutputMessages, convertVercelPromptToMessages } = require('../messages/vercel-ai')
|
|
6
|
+
const { SOURCE_AUTO } = require('../tags')
|
|
7
|
+
const { pushEvaluation } = require('./evaluate')
|
|
8
|
+
|
|
9
|
+
const doGenerateBeforeChannel = channel('dd-trace:vercel-ai:doGenerate:before')
|
|
10
|
+
const doGenerateAfterChannel = channel('dd-trace:vercel-ai:doGenerate:after')
|
|
11
|
+
const doStreamBeforeChannel = channel('dd-trace:vercel-ai:doStream:before')
|
|
12
|
+
const doStreamAfterChannel = channel('dd-trace:vercel-ai:doStream:after')
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Subscribes AI Guard to Vercel AI lifecycle channels.
|
|
16
|
+
*
|
|
17
|
+
* @param {object} aiguard
|
|
18
|
+
* @param {boolean} block
|
|
19
|
+
* @returns {() => void}
|
|
20
|
+
*/
|
|
21
|
+
function enable (aiguard, block) {
|
|
22
|
+
const opts = { block, source: SOURCE_AUTO, integration: 'ai' }
|
|
23
|
+
|
|
24
|
+
function onBefore (ctx) {
|
|
25
|
+
pushEvaluation(ctx, aiguard, convertVercelPromptToMessages(ctx.prompt), opts)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function onGenerateAfter (ctx) {
|
|
29
|
+
const inputMessages = convertVercelPromptToMessages(ctx.prompt)
|
|
30
|
+
if (!inputMessages.length || !ctx.result?.content?.length) return
|
|
31
|
+
|
|
32
|
+
pushEvaluation(ctx, aiguard, buildOutputMessages(inputMessages, ctx.result.content), opts)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function onStreamAfter (ctx) {
|
|
36
|
+
const inputMessages = convertVercelPromptToMessages(ctx.prompt)
|
|
37
|
+
if (!inputMessages.length || !ctx.chunks?.length) return
|
|
38
|
+
|
|
39
|
+
pushEvaluation(ctx, aiguard, buildOutputMessages(inputMessages, getStreamContent(ctx.chunks)), opts)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
doGenerateBeforeChannel.subscribe(onBefore)
|
|
43
|
+
doGenerateAfterChannel.subscribe(onGenerateAfter)
|
|
44
|
+
doStreamBeforeChannel.subscribe(onBefore)
|
|
45
|
+
doStreamAfterChannel.subscribe(onStreamAfter)
|
|
46
|
+
|
|
47
|
+
return function disable () {
|
|
48
|
+
doGenerateBeforeChannel.unsubscribe(onBefore)
|
|
49
|
+
doGenerateAfterChannel.unsubscribe(onGenerateAfter)
|
|
50
|
+
doStreamBeforeChannel.unsubscribe(onBefore)
|
|
51
|
+
doStreamAfterChannel.unsubscribe(onStreamAfter)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Converts Vercel stream chunks into the content shape used by doGenerate results.
|
|
57
|
+
*
|
|
58
|
+
* @param {Array<object>} chunks
|
|
59
|
+
* @returns {Array<object>}
|
|
60
|
+
*/
|
|
61
|
+
function getStreamContent (chunks) {
|
|
62
|
+
const toolCalls = []
|
|
63
|
+
const textParts = []
|
|
64
|
+
|
|
65
|
+
for (const chunk of chunks) {
|
|
66
|
+
if (chunk?.type === 'tool-call') {
|
|
67
|
+
toolCalls.push(chunk)
|
|
68
|
+
} else if (chunk?.type === 'text-delta') {
|
|
69
|
+
textParts.push(chunk.textDelta)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (toolCalls.length) return toolCalls
|
|
74
|
+
const text = textParts.join('')
|
|
75
|
+
return text ? [{ type: 'text', text }] : []
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
module.exports = { enable }
|