dd-trace 5.106.0 → 5.108.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/index.d.ts +20 -1
- package/package.json +9 -11
- package/packages/datadog-core/src/storage.js +47 -48
- package/packages/datadog-esbuild/index.js +6 -1
- package/packages/datadog-instrumentations/src/ai.js +12 -3
- package/packages/datadog-instrumentations/src/body-parser.js +5 -2
- package/packages/datadog-instrumentations/src/connect.js +3 -2
- package/packages/datadog-instrumentations/src/cookie-parser.js +3 -2
- package/packages/datadog-instrumentations/src/cucumber.js +7 -0
- package/packages/datadog-instrumentations/src/express-mongo-sanitize.js +7 -5
- package/packages/datadog-instrumentations/src/express-session.js +12 -11
- package/packages/datadog-instrumentations/src/express.js +24 -20
- package/packages/datadog-instrumentations/src/fastify.js +18 -6
- package/packages/datadog-instrumentations/src/helpers/openai-ai-guard.js +27 -12
- package/packages/datadog-instrumentations/src/http/client.js +9 -12
- package/packages/datadog-instrumentations/src/http/server.js +30 -16
- package/packages/datadog-instrumentations/src/http2/client.js +15 -12
- package/packages/datadog-instrumentations/src/http2/server.js +15 -8
- package/packages/datadog-instrumentations/src/jest/bail-reporter.js +42 -0
- package/packages/datadog-instrumentations/src/jest.js +143 -73
- package/packages/datadog-instrumentations/src/mocha/main.js +43 -8
- package/packages/datadog-instrumentations/src/mocha/utils.js +128 -17
- package/packages/datadog-instrumentations/src/multer.js +3 -2
- package/packages/datadog-instrumentations/src/mysql2.js +34 -0
- package/packages/datadog-instrumentations/src/net.js +8 -6
- package/packages/datadog-instrumentations/src/openai.js +19 -7
- package/packages/datadog-instrumentations/src/pg.js +19 -0
- package/packages/datadog-instrumentations/src/router.js +12 -10
- package/packages/datadog-instrumentations/src/vitest.js +29 -4
- package/packages/datadog-plugin-aws-sdk/src/base.js +0 -3
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +62 -11
- package/packages/datadog-plugin-cucumber/src/index.js +2 -0
- package/packages/datadog-plugin-cypress/src/support.js +31 -1
- package/packages/datadog-plugin-http/src/client.js +0 -3
- package/packages/datadog-plugin-http/src/server.js +11 -1
- package/packages/datadog-plugin-mocha/src/index.js +2 -0
- package/packages/datadog-plugin-pg/src/index.js +10 -0
- package/packages/dd-trace/src/aiguard/index.js +34 -15
- package/packages/dd-trace/src/aiguard/sdk.js +34 -3
- package/packages/dd-trace/src/aiguard/tags.js +6 -0
- package/packages/dd-trace/src/appsec/downstream_requests.js +3 -2
- package/packages/dd-trace/src/appsec/iast/index.js +3 -2
- package/packages/dd-trace/src/appsec/rasp/ssrf.js +2 -1
- package/packages/dd-trace/src/appsec/reporter.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +1 -1
- package/packages/dd-trace/src/config/defaults.js +14 -0
- package/packages/dd-trace/src/config/generated-config-types.d.ts +2 -1
- package/packages/dd-trace/src/config/helper.js +1 -0
- package/packages/dd-trace/src/config/index.js +5 -9
- package/packages/dd-trace/src/config/parsers.js +8 -0
- package/packages/dd-trace/src/config/supported-configurations.json +20 -6
- package/packages/dd-trace/src/crashtracking/crashtracker.js +2 -2
- package/packages/dd-trace/src/datastreams/writer.js +1 -2
- package/packages/dd-trace/src/debugger/config.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/config.js +3 -2
- package/packages/dd-trace/src/debugger/index.js +1 -2
- package/packages/dd-trace/src/dogstatsd.js +2 -3
- package/packages/dd-trace/src/encode/0.4.js +49 -41
- package/packages/dd-trace/src/encode/agentless-json.js +5 -1
- package/packages/dd-trace/src/encode/tags-processors.js +14 -0
- package/packages/dd-trace/src/exporters/agent/index.js +1 -2
- package/packages/dd-trace/src/exporters/agentless/index.js +6 -10
- package/packages/dd-trace/src/exporters/common/buffering-exporter.js +1 -2
- package/packages/dd-trace/src/exporters/common/request.js +26 -0
- package/packages/dd-trace/src/exporters/span-stats/index.js +1 -2
- package/packages/dd-trace/src/llmobs/plugins/genai/index.js +4 -0
- package/packages/dd-trace/src/llmobs/plugins/genai/util.js +45 -0
- package/packages/dd-trace/src/llmobs/sdk.js +4 -1
- package/packages/dd-trace/src/llmobs/span_processor.js +17 -1
- package/packages/dd-trace/src/llmobs/tagger.js +5 -3
- package/packages/dd-trace/src/llmobs/util.js +54 -0
- package/packages/dd-trace/src/llmobs/writers/base.js +1 -2
- package/packages/dd-trace/src/llmobs/writers/util.js +1 -2
- package/packages/dd-trace/src/openfeature/writers/base.js +1 -10
- package/packages/dd-trace/src/openfeature/writers/util.js +1 -2
- package/packages/dd-trace/src/opentelemetry/metrics/instruments.js +26 -13
- package/packages/dd-trace/src/opentelemetry/metrics/meter.js +7 -10
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +92 -0
- package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +3 -2
- package/packages/dd-trace/src/opentracing/span.js +23 -18
- package/packages/dd-trace/src/opentracing/tracer.js +16 -12
- package/packages/dd-trace/src/plugins/ci_plugin.js +131 -46
- package/packages/dd-trace/src/priority_sampler.js +6 -5
- package/packages/dd-trace/src/profiling/config.js +3 -2
- package/packages/dd-trace/src/profiling/profilers/events.js +26 -4
- package/packages/dd-trace/src/profiling/profilers/space.js +3 -1
- package/packages/dd-trace/src/proxy.js +13 -10
- package/packages/dd-trace/src/remote_config/index.js +1 -2
- package/packages/dd-trace/src/runtime_metrics/client.js +30 -0
- package/packages/dd-trace/src/runtime_metrics/index.js +12 -2
- package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +284 -0
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +2 -11
- package/packages/dd-trace/src/service-naming/source-resolver.js +5 -1
- package/packages/dd-trace/src/span_format.js +33 -25
- package/packages/dd-trace/src/span_stats.js +1 -1
- package/packages/dd-trace/src/startup-log.js +1 -2
- package/packages/dd-trace/src/telemetry/send-data.js +1 -1
- package/packages/dd-trace/src/tracer.js +1 -1
- package/vendor/dist/@apm-js-collab/code-transformer/index.js +2 -2
- 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/vendor/dist/shell-quote/index.js +1 -1
- package/packages/dd-trace/src/agent/url.js +0 -28
- package/scripts/preinstall.js +0 -34
package/index.d.ts
CHANGED
|
@@ -19,6 +19,12 @@ interface Tracer extends opentracing.Tracer {
|
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* Starts and returns a new Span representing a logical unit of work.
|
|
22
|
+
*
|
|
23
|
+
* The returned span is not activated on the current scope. Spans created
|
|
24
|
+
* while it is open — via {@link Tracer.trace} or auto-instrumentation — only
|
|
25
|
+
* nest under it when it is the active span, so wrap the work in
|
|
26
|
+
* {@link Scope.activate} (or use {@link Tracer.trace}) when child spans
|
|
27
|
+
* should descend from it.
|
|
22
28
|
* @param {string} name The name of the operation.
|
|
23
29
|
* @param {tracer.SpanOptions} [options] Options for the newly created span.
|
|
24
30
|
* @returns {Span} A new Span object.
|
|
@@ -4000,6 +4006,14 @@ declare namespace tracer {
|
|
|
4000
4006
|
template?: string | Message[]
|
|
4001
4007
|
}
|
|
4002
4008
|
|
|
4009
|
+
interface ToolDefinition {
|
|
4010
|
+
name : string,
|
|
4011
|
+
description? : string,
|
|
4012
|
+
schema? : {[key : string] : any}
|
|
4013
|
+
version? : string
|
|
4014
|
+
}
|
|
4015
|
+
|
|
4016
|
+
|
|
4003
4017
|
/**
|
|
4004
4018
|
* Annotation options for LLM Observability spans.
|
|
4005
4019
|
*/
|
|
@@ -4046,6 +4060,12 @@ declare namespace tracer {
|
|
|
4046
4060
|
* A Prompt object that represents the prompt used for an LLM call. Only used on `llm` spans.
|
|
4047
4061
|
*/
|
|
4048
4062
|
prompt?: Prompt,
|
|
4063
|
+
|
|
4064
|
+
/**
|
|
4065
|
+
* A list of ToolDefinition object that represents the tools available to the LLM for this span
|
|
4066
|
+
* Each definition requires a `name` and optionally accepts `description`, `schema`, and `version`.
|
|
4067
|
+
* */
|
|
4068
|
+
toolDefinitions?: ToolDefinition[]
|
|
4049
4069
|
}
|
|
4050
4070
|
|
|
4051
4071
|
interface AnnotationContextOptions {
|
|
@@ -4161,7 +4181,6 @@ declare namespace tracer {
|
|
|
4161
4181
|
*/
|
|
4162
4182
|
agentlessEnabled?: boolean,
|
|
4163
4183
|
}
|
|
4164
|
-
|
|
4165
4184
|
/** @hidden */
|
|
4166
4185
|
type spanKind = 'agent' | 'workflow' | 'task' | 'tool' | 'retrieval' | 'embedding' | 'llm'
|
|
4167
4186
|
}
|
package/package.json
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dd-trace",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.108.0",
|
|
4
4
|
"description": "Datadog APM tracing client for JavaScript",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"typings": "index.d.ts",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"env": "bash ./plugin-env",
|
|
9
9
|
"prepare": "node scripts/patch-istanbul-lib-coverage.js && cd vendor && npm ci --include=dev",
|
|
10
|
-
"preinstall": "node scripts/preinstall.js",
|
|
11
10
|
"prepack": "node scripts/release/swap-v5-types.js",
|
|
12
11
|
"bench": "node benchmark/index.js",
|
|
13
12
|
"bench:e2e:test-optimization": "node benchmark/e2e-test-optimization/benchmark-run.js",
|
|
@@ -23,7 +22,7 @@
|
|
|
23
22
|
"lint:fix": "node scripts/check_licenses.js && node scripts/check-no-coverage-artifacts.js && node scripts/check-no-mcr-images.js && node scripts/check-docker-image-shas.js && eslint . --concurrency=auto --max-warnings 0 --fix",
|
|
24
23
|
"lint:inspect": "npx @eslint/config-inspector@latest",
|
|
25
24
|
"lint:codeowners": "codeowners-audit",
|
|
26
|
-
"lint:codeowners:ci": "codeowners-audit --glob='**/*.spec.js' --glob='benchmark/sirun/**'",
|
|
25
|
+
"lint:codeowners:ci": "codeowners-audit --glob='**/*.spec.js' --glob='benchmark/sirun/**' --glob='.agents/**' --glob='.claude/**'",
|
|
27
26
|
"release:proposal": "node scripts/release/proposal",
|
|
28
27
|
"services": "node ./scripts/install_plugin_modules && node packages/dd-trace/test/setup/services",
|
|
29
28
|
"test": "echo '\nError: The root \"npm test\" command is intentionally disabled.\n\nInstead, run specific test suites:\n - npm run test:trace:core\n - npm run test:appsec\n - etc.\n\nOr run individual test files:\n npx mocha path/to/test.spec.js\n\nSee CONTRIBUTING.md (Testing section) for more details.\n' && exit 1",
|
|
@@ -153,7 +152,6 @@
|
|
|
153
152
|
"packages/datadog-instrumentations/orchestrion.yml",
|
|
154
153
|
"README.md",
|
|
155
154
|
"register.js",
|
|
156
|
-
"scripts/preinstall.js",
|
|
157
155
|
"vendor/dist/**/*.d.ts",
|
|
158
156
|
"vendor/dist/**/*.js",
|
|
159
157
|
"vendor/dist/**/*.wasm",
|
|
@@ -166,12 +164,12 @@
|
|
|
166
164
|
"opentracing": ">=0.14.7"
|
|
167
165
|
},
|
|
168
166
|
"optionalDependencies": {
|
|
169
|
-
"@datadog/libdatadog": "0.9.
|
|
167
|
+
"@datadog/libdatadog": "0.9.4",
|
|
170
168
|
"@datadog/native-appsec": "11.0.1",
|
|
171
169
|
"@datadog/native-iast-taint-tracking": "4.2.0",
|
|
172
170
|
"@datadog/native-metrics": "3.1.2",
|
|
173
|
-
"@datadog/openfeature-node-server": "
|
|
174
|
-
"@datadog/pprof": "5.
|
|
171
|
+
"@datadog/openfeature-node-server": "2.0.0",
|
|
172
|
+
"@datadog/pprof": "5.15.0",
|
|
175
173
|
"@datadog/wasm-js-rewriter": "5.0.1",
|
|
176
174
|
"@opentelemetry/api": ">=1.0.0 <1.10.0",
|
|
177
175
|
"@opentelemetry/api-logs": "<1.0.0",
|
|
@@ -180,7 +178,7 @@
|
|
|
180
178
|
"devDependencies": {
|
|
181
179
|
"@actions/core": "^3.0.1",
|
|
182
180
|
"@actions/github": "^9.1.1",
|
|
183
|
-
"@babel/helpers": "^7.29.
|
|
181
|
+
"@babel/helpers": "^7.29.7",
|
|
184
182
|
"@eslint/eslintrc": "^3.3.5",
|
|
185
183
|
"@eslint/js": "^9.39.2",
|
|
186
184
|
"@msgpack/msgpack": "^3.1.3",
|
|
@@ -190,7 +188,7 @@
|
|
|
190
188
|
"@types/mocha": "^10.0.10",
|
|
191
189
|
"@types/node": "^18.19.106",
|
|
192
190
|
"@types/sinon": "^21.0.1",
|
|
193
|
-
"axios": "^1.
|
|
191
|
+
"axios": "^1.17.0",
|
|
194
192
|
"benchmark": "^2.1.4",
|
|
195
193
|
"body-parser": "^2.2.2",
|
|
196
194
|
"bun": "1.3.14",
|
|
@@ -198,7 +196,7 @@
|
|
|
198
196
|
"eslint": "^9.39.2",
|
|
199
197
|
"eslint-plugin-cypress": "^6.4.1",
|
|
200
198
|
"eslint-plugin-import": "^2.32.0",
|
|
201
|
-
"eslint-plugin-jsdoc": "^63.0.
|
|
199
|
+
"eslint-plugin-jsdoc": "^63.0.1",
|
|
202
200
|
"eslint-plugin-mocha": "^11.3.0",
|
|
203
201
|
"eslint-plugin-n": "^18.0.1",
|
|
204
202
|
"eslint-plugin-promise": "^7.3.0",
|
|
@@ -224,7 +222,7 @@
|
|
|
224
222
|
"proxyquire": "^2.1.3",
|
|
225
223
|
"retry": "^0.13.1",
|
|
226
224
|
"semifies": "^1.0.0",
|
|
227
|
-
"semver": "^7.8.
|
|
225
|
+
"semver": "^7.8.2",
|
|
228
226
|
"sinon": "^22.0.0",
|
|
229
227
|
"tiktoken": "^1.0.21",
|
|
230
228
|
"typescript": "^6.0.3",
|
|
@@ -3,37 +3,26 @@
|
|
|
3
3
|
const { AsyncLocalStorage } = require('async_hooks')
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
6
|
+
* `AsyncLocalStorage` with a `getHandle()` escape hatch: a span stashes the
|
|
7
|
+
* active handle at creation (see opentracing/span.js) so a later context can
|
|
8
|
+
* recover its store without holding the store itself.
|
|
9
|
+
*
|
|
10
|
+
* Under AsyncContextFrame — Node's default ALS backend on every release that
|
|
11
|
+
* ships it — the active value lives in the context frame and is never written
|
|
12
|
+
* to the async resource, so the store is held directly and the handle is the
|
|
13
|
+
* store. Pre-ACF `async_hooks` instead pinned the value onto the resource
|
|
14
|
+
* object, where it was visible to logging and retained with the resource
|
|
15
|
+
* graph; the `!isACFActive` block below restores the original WeakMap-handle
|
|
16
|
+
* indirection that kept the real store off the resource on those runtimes.
|
|
12
17
|
*
|
|
13
18
|
* @template T
|
|
14
19
|
* @typedef {Record<string, T>} Store
|
|
15
20
|
*/
|
|
16
21
|
class DatadogStorage extends AsyncLocalStorage {
|
|
17
22
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
|
|
21
|
-
enterWith (store) {
|
|
22
|
-
const handle = { noop: store?.noop }
|
|
23
|
-
stores.set(handle, store)
|
|
24
|
-
super.enterWith(handle)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* This is method is a passthrough to the real `getStore()`, so that, when we
|
|
29
|
-
* need it, we can use the handle rather than our mapped store.
|
|
30
|
-
*
|
|
31
|
-
* It's only here because stores are currently used for a bunch of things,
|
|
32
|
-
* and we don't want to hold on to all of them in spans
|
|
33
|
-
* (see opentracing/span.js). Using a namespaced storage for spans would
|
|
34
|
-
* solve this.
|
|
35
|
-
*
|
|
36
|
-
* TODO: Refactor the Scope class to use a span-only store and remove this.
|
|
23
|
+
* Passthrough to the real `getStore()`. A span stashes this handle and feeds
|
|
24
|
+
* it back to `getStore(handle)` later. Identical in both modes: under ACF the
|
|
25
|
+
* handle is the store; without ACF it is the WeakMap key.
|
|
37
26
|
*
|
|
38
27
|
* @returns {Store<unknown>}
|
|
39
28
|
*/
|
|
@@ -42,22 +31,13 @@ class DatadogStorage extends AsyncLocalStorage {
|
|
|
42
31
|
}
|
|
43
32
|
|
|
44
33
|
/**
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* retrieved through `getHandle()` can also be passed in to be used as the
|
|
48
|
-
* key. This is useful if you've stashed a handle somewhere and want to
|
|
49
|
-
* retrieve the store with it.
|
|
50
|
-
* @param {object} [handle]
|
|
34
|
+
* @param {Store<unknown>} [handle] A handle from `getHandle()`; defaults to
|
|
35
|
+
* the active one. Under ACF the handle is the store, so it is returned as-is.
|
|
51
36
|
* @returns {Store<unknown> | undefined}
|
|
52
37
|
* @override
|
|
53
38
|
*/
|
|
54
39
|
getStore (handle) {
|
|
55
|
-
|
|
56
|
-
handle = super.getStore()
|
|
57
|
-
}
|
|
58
|
-
if (handle) {
|
|
59
|
-
return stores.get(handle)
|
|
60
|
-
}
|
|
40
|
+
return handle ?? super.getStore()
|
|
61
41
|
}
|
|
62
42
|
}
|
|
63
43
|
|
|
@@ -77,12 +57,37 @@ if (!isACFActive) {
|
|
|
77
57
|
const superGetStore = AsyncLocalStorage.prototype.getStore
|
|
78
58
|
const superEnterWith = AsyncLocalStorage.prototype.enterWith
|
|
79
59
|
|
|
60
|
+
// Without ACF, ALS writes the entered value onto the async resource. Keep the
|
|
61
|
+
// real store off the resource by entering a small handle and mapping it to
|
|
62
|
+
// the store through a WeakMap, then reversing the lookup on read.
|
|
63
|
+
const stores = new WeakMap()
|
|
64
|
+
|
|
80
65
|
/**
|
|
81
|
-
*
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
66
|
+
* @param {Store<unknown>} [store]
|
|
67
|
+
*/
|
|
68
|
+
DatadogStorage.prototype.enterWith = function enterWith (store) {
|
|
69
|
+
const handle = { noop: store?.noop }
|
|
70
|
+
stores.set(handle, store)
|
|
71
|
+
superEnterWith.call(this, handle)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @param {object} [handle]
|
|
76
|
+
* @returns {Store<unknown> | undefined}
|
|
77
|
+
*/
|
|
78
|
+
DatadogStorage.prototype.getStore = function getStore (handle) {
|
|
79
|
+
if (!handle) {
|
|
80
|
+
handle = superGetStore.call(this)
|
|
81
|
+
}
|
|
82
|
+
if (handle) {
|
|
83
|
+
return stores.get(handle)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Without ACF, `super.run()` does not delegate to `enterWith()`, so the
|
|
89
|
+
* WeakMap handle is never created and `getStore()` would miss. Drive the
|
|
90
|
+
* handle path manually and restore the prior handle on the way out.
|
|
86
91
|
*
|
|
87
92
|
* @template R
|
|
88
93
|
* @template {unknown[]} TArgs
|
|
@@ -103,12 +108,6 @@ if (!isACFActive) {
|
|
|
103
108
|
}
|
|
104
109
|
}
|
|
105
110
|
|
|
106
|
-
/**
|
|
107
|
-
* This is the map from handles to real stores, used in the class above.
|
|
108
|
-
* @type {WeakMap<WeakKey, Store<unknown>|undefined>}
|
|
109
|
-
*/
|
|
110
|
-
const stores = new WeakMap()
|
|
111
|
-
|
|
112
111
|
/**
|
|
113
112
|
* For convenience, we use the `storage` function as a registry of namespaces
|
|
114
113
|
* corresponding to DatadogStorage instances. This lets us have separate
|
|
@@ -347,10 +347,15 @@ register(${JSON.stringify(toRegister)}, _, set, get, ${JSON.stringify(data.raw)}
|
|
|
347
347
|
}
|
|
348
348
|
} else {
|
|
349
349
|
const fileCode = fs.readFileSync(args.path, 'utf8')
|
|
350
|
+
// Don't spread `...arguments`: esbuild's minifier can rewrite the surrounding
|
|
351
|
+
// `__commonJS` factory into an arrow function whose `arguments` resolves to the
|
|
352
|
+
// ESM top-level scope (see issue #8681). Pass `(module.exports, module)`
|
|
353
|
+
// explicitly; the IIFE declares no parameters so esbuild's static `require()`
|
|
354
|
+
// resolution inside `fileCode` is preserved through the factory's closure.
|
|
350
355
|
contents = `
|
|
351
356
|
(function() {
|
|
352
357
|
${fileCode}
|
|
353
|
-
})(
|
|
358
|
+
})(module.exports, module);
|
|
354
359
|
{
|
|
355
360
|
const dc = require('dc-polyfill');
|
|
356
361
|
const ch = dc.channel('${CHANNEL}');
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
const { channel, tracingChannel } = require('dc-polyfill')
|
|
4
4
|
const shimmer = require('../../datadog-shimmer')
|
|
5
|
-
const { addHook, getHooks } = require('./helpers/instrument')
|
|
6
5
|
const { convertVercelPromptToMessages, buildOutputMessages } = require('./helpers/ai-messages')
|
|
6
|
+
const { addHook, getHooks } = require('./helpers/instrument')
|
|
7
7
|
|
|
8
8
|
const vercelAiTracingChannel = tracingChannel('dd-trace:vercel-ai')
|
|
9
9
|
const vercelAiSpanSetAttributesChannel = channel('dd-trace:vercel-ai:span:setAttributes')
|
|
@@ -15,12 +15,21 @@ const wrappedModels = new WeakSet()
|
|
|
15
15
|
/**
|
|
16
16
|
* Publishes already-converted AI-style messages to the AI Guard evaluation channel.
|
|
17
17
|
*
|
|
18
|
+
* Subscribers push async work into `pending` and abort `abortController` to block.
|
|
19
|
+
*
|
|
18
20
|
* @param {Array<object>} messages - AI-style messages to evaluate.
|
|
19
21
|
* @returns {Promise<void>}
|
|
20
22
|
*/
|
|
21
23
|
function publishEvaluation (messages) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
const abortController = new AbortController()
|
|
25
|
+
const ctx = { messages, integration: 'ai', abortController, pending: [] }
|
|
26
|
+
|
|
27
|
+
aiguardChannel.publish(ctx)
|
|
28
|
+
|
|
29
|
+
return Promise.all(ctx.pending).then(() => {
|
|
30
|
+
if (abortController.signal.aborted) {
|
|
31
|
+
throw abortController.signal.reason
|
|
32
|
+
}
|
|
24
33
|
})
|
|
25
34
|
}
|
|
26
35
|
|
|
@@ -6,7 +6,10 @@ const { channel, addHook, AsyncResource } = require('./helpers/instrument')
|
|
|
6
6
|
const bodyParserReadCh = channel('datadog:body-parser:read:finish')
|
|
7
7
|
|
|
8
8
|
function publishRequestBodyAndNext (req, res, next) {
|
|
9
|
-
|
|
9
|
+
// Named `next`/arity-1 mirrors the express continuation so wrapCallback skips
|
|
10
|
+
// its name/length rewrite; `_error` only pads the arity and `arguments`
|
|
11
|
+
// forwards whatever next was called with.
|
|
12
|
+
return shimmer.wrapCallback(next, original => function next (_error) {
|
|
10
13
|
if (bodyParserReadCh.hasSubscribers && req) {
|
|
11
14
|
const abortController = new AbortController()
|
|
12
15
|
const body = req.body
|
|
@@ -16,7 +19,7 @@ function publishRequestBodyAndNext (req, res, next) {
|
|
|
16
19
|
if (abortController.signal.aborted) return
|
|
17
20
|
}
|
|
18
21
|
|
|
19
|
-
return
|
|
22
|
+
return original.apply(this, arguments)
|
|
20
23
|
})
|
|
21
24
|
}
|
|
22
25
|
|
|
@@ -90,7 +90,8 @@ function wrapLayerHandle (layer) {
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
function wrapNext (req, next) {
|
|
93
|
-
|
|
93
|
+
// Mirror next's name/arity so wrapCallback skips its per-call identity rewrite.
|
|
94
|
+
return shimmer.wrapCallback(next, original => function next (error) {
|
|
94
95
|
if (error) {
|
|
95
96
|
errorChannel.publish({ req, error })
|
|
96
97
|
}
|
|
@@ -98,7 +99,7 @@ function wrapNext (req, next) {
|
|
|
98
99
|
nextChannel.publish({ req })
|
|
99
100
|
finishChannel.publish({ req })
|
|
100
101
|
|
|
101
|
-
|
|
102
|
+
original.apply(this, arguments)
|
|
102
103
|
})
|
|
103
104
|
}
|
|
104
105
|
|
|
@@ -6,7 +6,8 @@ const { channel, addHook } = require('./helpers/instrument')
|
|
|
6
6
|
const cookieParserReadCh = channel('datadog:cookie-parser:read:finish')
|
|
7
7
|
|
|
8
8
|
function publishRequestCookieAndNext (req, res, next) {
|
|
9
|
-
|
|
9
|
+
// Mirror next's name/arity so wrapCallback skips its per-call identity rewrite.
|
|
10
|
+
return shimmer.wrapCallback(next, original => function next (_error) {
|
|
10
11
|
if (cookieParserReadCh.hasSubscribers && req) {
|
|
11
12
|
const abortController = new AbortController()
|
|
12
13
|
|
|
@@ -17,7 +18,7 @@ function publishRequestCookieAndNext (req, res, next) {
|
|
|
17
18
|
if (abortController.signal.aborted) return
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
return
|
|
21
|
+
return original.apply(this, arguments)
|
|
21
22
|
})
|
|
22
23
|
}
|
|
23
24
|
|
|
@@ -1252,6 +1252,11 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
|
|
|
1252
1252
|
numRetriesByPickleId.set(pickle.id, 0)
|
|
1253
1253
|
}
|
|
1254
1254
|
}
|
|
1255
|
+
const originalRetry = this.options.retry
|
|
1256
|
+
const isManagedRetry = isAttemptToFix || (isEarlyFlakeDetectionEnabled && (isNew || isModified))
|
|
1257
|
+
if (isManagedRetry) {
|
|
1258
|
+
this.options.retry = 0
|
|
1259
|
+
}
|
|
1255
1260
|
// TODO: for >=11 we could use `runTestCaseResult` instead of accumulating results in `lastStatusByPickleId`
|
|
1256
1261
|
const firstExecutionStart = performance.now()
|
|
1257
1262
|
let runTestCaseResult = await runTestCaseFunction.apply(this, arguments)
|
|
@@ -1351,6 +1356,8 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
|
|
|
1351
1356
|
testSuiteFinishCh.publish({ status: testSuiteStatus, testSuitePath })
|
|
1352
1357
|
}
|
|
1353
1358
|
|
|
1359
|
+
this.options.retry = originalRetry
|
|
1360
|
+
|
|
1354
1361
|
if (isNewerCucumberVersion && isEarlyFlakeDetectionEnabled && (isNew || isModified)) {
|
|
1355
1362
|
return shouldBePassedByEFD
|
|
1356
1363
|
}
|
|
@@ -25,21 +25,23 @@ addHook({ name: 'express-mongo-sanitize', versions: ['>=1.0.0'] }, expressMongoS
|
|
|
25
25
|
return shimmer.wrapFunction(expressMongoSanitize, expressMongoSanitize => function (...args) {
|
|
26
26
|
const middleware = expressMongoSanitize.apply(this, args)
|
|
27
27
|
|
|
28
|
-
return shimmer.wrapFunction(middleware, middleware => function (
|
|
28
|
+
return shimmer.wrapFunction(middleware, middleware => function (...args) {
|
|
29
29
|
if (!sanitizeMiddlewareFinished.hasSubscribers) {
|
|
30
|
-
return
|
|
30
|
+
return Reflect.apply(middleware, this, args)
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
const
|
|
33
|
+
const req = args[0]
|
|
34
|
+
// Mirror next's name/arity so wrapCallback skips its per-call identity rewrite.
|
|
35
|
+
const wrappedNext = shimmer.wrapCallback(args[2], original => function next (_error) {
|
|
34
36
|
sanitizeMiddlewareFinished.publish({
|
|
35
37
|
sanitizedProperties: propertiesToSanitize,
|
|
36
38
|
req,
|
|
37
39
|
})
|
|
38
40
|
|
|
39
|
-
return
|
|
41
|
+
return original.apply(this, arguments)
|
|
40
42
|
})
|
|
41
43
|
|
|
42
|
-
return middleware.call(this, req,
|
|
44
|
+
return middleware.call(this, req, args[1], wrappedNext)
|
|
43
45
|
})
|
|
44
46
|
})
|
|
45
47
|
})
|
|
@@ -6,22 +6,23 @@ const { channel, addHook } = require('./helpers/instrument')
|
|
|
6
6
|
const sessionMiddlewareFinishCh = channel('datadog:express-session:middleware:finish')
|
|
7
7
|
|
|
8
8
|
function wrapSessionMiddleware (sessionMiddleware) {
|
|
9
|
-
return function wrappedSessionMiddleware (
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
return function wrappedSessionMiddleware (...args) {
|
|
10
|
+
const req = args[0]
|
|
11
|
+
const res = args[1]
|
|
12
|
+
// Mirror next's name/arity so wrapCallback skips its per-call identity rewrite.
|
|
13
|
+
args[2] = shimmer.wrapCallback(args[2], original => function next (_error) {
|
|
14
|
+
if (sessionMiddlewareFinishCh.hasSubscribers) {
|
|
15
|
+
const abortController = new AbortController()
|
|
14
16
|
|
|
15
|
-
|
|
17
|
+
sessionMiddlewareFinishCh.publish({ req, res, sessionId: req.sessionID, abortController })
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return next.apply(this, args)
|
|
19
|
+
if (abortController.signal.aborted) return
|
|
21
20
|
}
|
|
21
|
+
|
|
22
|
+
return original.apply(this, arguments)
|
|
22
23
|
})
|
|
23
24
|
|
|
24
|
-
return
|
|
25
|
+
return Reflect.apply(sessionMiddleware, this, args)
|
|
25
26
|
}
|
|
26
27
|
}
|
|
27
28
|
|
|
@@ -18,57 +18,60 @@ const handleChannel = channel('apm:express:request:handle')
|
|
|
18
18
|
const routeAddedChannel = channel('apm:express:route:added')
|
|
19
19
|
|
|
20
20
|
function wrapHandle (handle) {
|
|
21
|
-
return function handleWithTrace (
|
|
21
|
+
return function handleWithTrace (...args) {
|
|
22
22
|
if (handleChannel.hasSubscribers) {
|
|
23
|
-
handleChannel.publish({ req })
|
|
23
|
+
handleChannel.publish({ req: args[0] })
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
return
|
|
26
|
+
return Reflect.apply(handle, this, args)
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
const responseJsonChannel = channel('datadog:express:response:json:start')
|
|
31
31
|
|
|
32
32
|
function wrapResponseJson (json) {
|
|
33
|
-
return function wrappedJson (
|
|
33
|
+
return function wrappedJson (...args) {
|
|
34
34
|
if (responseJsonChannel.hasSubscribers) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
let obj = args[0]
|
|
36
|
+
// Deprecated express 3.x res.json(status, body) form, still honored by 4.x but not
|
|
37
|
+
// exercised by any suite (the unit harness can't drive res.json's freshness path).
|
|
38
|
+
/* istanbul ignore if */
|
|
39
|
+
if (args.length === 2 && typeof args[1] !== 'number') {
|
|
40
|
+
obj = args[1]
|
|
38
41
|
}
|
|
39
42
|
|
|
40
43
|
responseJsonChannel.publish({ req: this.req, res: this, body: obj })
|
|
41
44
|
}
|
|
42
45
|
|
|
43
|
-
return
|
|
46
|
+
return Reflect.apply(json, this, args)
|
|
44
47
|
}
|
|
45
48
|
}
|
|
46
49
|
|
|
47
50
|
const responseRenderChannel = tracingChannel('datadog:express:response:render')
|
|
48
51
|
|
|
49
52
|
function wrapResponseRender (render) {
|
|
50
|
-
return function wrappedRender (
|
|
53
|
+
return function wrappedRender (...args) {
|
|
51
54
|
if (!responseRenderChannel.start.hasSubscribers) {
|
|
52
|
-
return
|
|
55
|
+
return Reflect.apply(render, this, args)
|
|
53
56
|
}
|
|
54
57
|
|
|
55
58
|
const abortController = new AbortController()
|
|
56
59
|
return responseRenderChannel.traceSync(
|
|
57
|
-
function (...
|
|
60
|
+
function (...renderArgs) {
|
|
58
61
|
if (abortController.signal.aborted) {
|
|
59
62
|
throw abortController.signal.reason || new Error('Aborted')
|
|
60
63
|
}
|
|
61
64
|
|
|
62
|
-
return
|
|
65
|
+
return Reflect.apply(render, this, renderArgs)
|
|
63
66
|
},
|
|
64
67
|
{
|
|
65
68
|
req: this.req,
|
|
66
|
-
view,
|
|
67
|
-
options,
|
|
69
|
+
view: args[0],
|
|
70
|
+
options: args[1],
|
|
68
71
|
abortController,
|
|
69
72
|
},
|
|
70
73
|
this,
|
|
71
|
-
...
|
|
74
|
+
...args
|
|
72
75
|
)
|
|
73
76
|
}
|
|
74
77
|
}
|
|
@@ -172,7 +175,8 @@ addHook({ name: 'express', versions: ['4'], file: 'lib/express.js' }, express =>
|
|
|
172
175
|
const queryParserReadCh = channel('datadog:query:read:finish')
|
|
173
176
|
|
|
174
177
|
function publishQueryParsedAndNext (req, res, next) {
|
|
175
|
-
|
|
178
|
+
// Mirror next's name/arity so wrapCallback skips its per-call identity rewrite.
|
|
179
|
+
return shimmer.wrapCallback(next, original => function next (_error) {
|
|
176
180
|
if (queryParserReadCh.hasSubscribers && req) {
|
|
177
181
|
const abortController = new AbortController()
|
|
178
182
|
const query = req.query
|
|
@@ -182,7 +186,7 @@ function publishQueryParsedAndNext (req, res, next) {
|
|
|
182
186
|
if (abortController.signal.aborted) return
|
|
183
187
|
}
|
|
184
188
|
|
|
185
|
-
return
|
|
189
|
+
return original.apply(this, arguments)
|
|
186
190
|
})
|
|
187
191
|
}
|
|
188
192
|
|
|
@@ -194,9 +198,9 @@ addHook({
|
|
|
194
198
|
return shimmer.wrapFunction(query, query => function (...args) {
|
|
195
199
|
const queryMiddleware = query.apply(this, args)
|
|
196
200
|
|
|
197
|
-
return shimmer.wrapFunction(queryMiddleware, queryMiddleware => function (
|
|
198
|
-
|
|
199
|
-
return
|
|
201
|
+
return shimmer.wrapFunction(queryMiddleware, queryMiddleware => function (...args) {
|
|
202
|
+
args[2] = publishQueryParsedAndNext(args[0], args[1], args[2])
|
|
203
|
+
return Reflect.apply(queryMiddleware, this, args)
|
|
200
204
|
})
|
|
201
205
|
})
|
|
202
206
|
})
|
|
@@ -207,11 +207,14 @@ function preHandler (request, reply, done) {
|
|
|
207
207
|
|
|
208
208
|
function preValidation (request, reply, done) {
|
|
209
209
|
const req = getReq(request)
|
|
210
|
-
const res = getRes(reply)
|
|
211
210
|
const ctx = parsingContexts.get(req)
|
|
212
|
-
ctx.res = res
|
|
213
211
|
|
|
214
|
-
|
|
212
|
+
// No stored context means the onRequest/preParsing fast path ran (no error /
|
|
213
|
+
// cookie / callback subscribers), so there is nothing to publish on; forward
|
|
214
|
+
// `done` instead of dereferencing a missing ctx in processInContext.
|
|
215
|
+
if (!ctx) return done()
|
|
216
|
+
|
|
217
|
+
ctx.res = getRes(reply)
|
|
215
218
|
|
|
216
219
|
preValidationCh.runStores(ctx, processInContext, undefined, request, ctx, done, req)
|
|
217
220
|
}
|
|
@@ -276,7 +279,7 @@ function wrapSend (send, req) {
|
|
|
276
279
|
const ctx = { req }
|
|
277
280
|
if (payload instanceof Error) {
|
|
278
281
|
ctx.error = payload
|
|
279
|
-
|
|
282
|
+
publishError(ctx)
|
|
280
283
|
} else if (canPublishResponsePayload(payload)) {
|
|
281
284
|
const res = getRes(this)
|
|
282
285
|
ctx.res = res
|
|
@@ -300,9 +303,18 @@ function getRouteConfig (request) {
|
|
|
300
303
|
return request?.routeOptions?.config
|
|
301
304
|
}
|
|
302
305
|
|
|
306
|
+
let publishingError = false
|
|
307
|
+
|
|
303
308
|
function publishError (ctx) {
|
|
304
|
-
|
|
305
|
-
|
|
309
|
+
// `errorChannel` is public: a subscriber that re-enters the hook pipeline while
|
|
310
|
+
// handling the error republishes here and recurses until the stack overflows.
|
|
311
|
+
if (ctx.error && !publishingError) {
|
|
312
|
+
publishingError = true
|
|
313
|
+
try {
|
|
314
|
+
errorChannel.publish(ctx)
|
|
315
|
+
} finally {
|
|
316
|
+
publishingError = false
|
|
317
|
+
}
|
|
306
318
|
}
|
|
307
319
|
|
|
308
320
|
return ctx.error
|