dd-trace 6.14.0 → 6.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -0
- package/ci/test-optimization-validation/framework-adapters/cucumber.js +4 -2
- package/ci/test-optimization-validation/package-check.js +4 -1
- package/ci/test-optimization-validation/scenarios/ci-wiring.js +1 -1
- package/index.d.ts +12 -0
- package/next.d.ts +27 -0
- package/next.js +3 -0
- package/package.json +17 -13
- package/packages/datadog-esbuild/index.js +14 -1
- package/packages/datadog-esbuild/src/resolver.js +284 -0
- package/packages/datadog-esbuild/src/utils.js +163 -119
- package/packages/datadog-instrumentations/src/ai.js +39 -97
- package/packages/datadog-instrumentations/src/anthropic.js +76 -134
- package/packages/datadog-instrumentations/src/aws-durable-execution-sdk-js.js +1 -1
- package/packages/datadog-instrumentations/src/azure-cosmos.js +1 -1
- package/packages/datadog-instrumentations/src/azure-durable-functions.js +23 -9
- package/packages/datadog-instrumentations/src/azure-functions.js +48 -0
- package/packages/datadog-instrumentations/src/bullmq.js +1 -1
- package/packages/datadog-instrumentations/src/claude-agent-sdk.js +1 -1
- package/packages/datadog-instrumentations/src/dd-trace-api.js +2 -2
- package/packages/datadog-instrumentations/src/graphql.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/bundler-register.js +39 -14
- package/packages/datadog-instrumentations/src/helpers/instrument.js +25 -8
- package/packages/datadog-instrumentations/src/helpers/instrumentation-utils.js +72 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +9 -64
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +46 -14
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/azure-durable-functions.js +17 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +1 -5
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +6 -7
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +99 -70
- package/packages/datadog-instrumentations/src/jest.js +141 -25
- package/packages/datadog-instrumentations/src/langchain.js +1 -1
- package/packages/datadog-instrumentations/src/langgraph.js +1 -1
- package/packages/datadog-instrumentations/src/mercurius.js +1 -1
- package/packages/datadog-instrumentations/src/mocha/main.js +41 -5
- package/packages/datadog-instrumentations/src/modelcontextprotocol-sdk.js +1 -1
- package/packages/datadog-instrumentations/src/openai-agents.js +1 -1
- package/packages/datadog-instrumentations/src/openai.js +54 -76
- package/packages/datadog-instrumentations/src/playwright.js +4 -0
- package/packages/datadog-instrumentations/src/vitest-worker.js +7 -10
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +7 -1
- package/packages/datadog-plugin-azure-cosmos/src/index.js +1 -1
- package/packages/datadog-plugin-azure-durable-functions/src/index.js +96 -1
- package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -24
- package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +2 -36
- package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-request-context.js +31 -0
- package/packages/datadog-plugin-graphql/src/resolve-error.js +33 -11
- package/packages/datadog-turbopack/index.js +212 -0
- package/packages/datadog-turbopack/src/constants.js +5 -0
- package/packages/datadog-turbopack/src/loader.js +500 -0
- package/packages/dd-trace/src/aiguard/index.js +2 -1
- package/packages/dd-trace/src/aiguard/integrations/anthropic.js +53 -25
- package/packages/dd-trace/src/aiguard/integrations/evaluate.js +49 -25
- package/packages/dd-trace/src/aiguard/integrations/index.js +3 -2
- package/packages/dd-trace/src/aiguard/integrations/openai.js +59 -30
- package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +116 -42
- package/packages/dd-trace/src/aiguard/messages/utils.js +29 -1
- package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +25 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/command-sensitive-analyzer.js +3 -1
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +531 -70
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +2 -2
- package/packages/dd-trace/src/appsec/telemetry/api_security.js +1 -3
- package/packages/dd-trace/src/appsec/telemetry/common.js +4 -0
- package/packages/dd-trace/src/appsec/telemetry/index.js +23 -5
- package/packages/dd-trace/src/appsec/telemetry/rasp.js +1 -20
- package/packages/dd-trace/src/appsec/telemetry/user.js +1 -3
- package/packages/dd-trace/src/appsec/telemetry/waf.js +1 -20
- package/packages/dd-trace/src/ci-visibility/requests/request.js +11 -0
- package/packages/dd-trace/src/ci-visibility/requests/video-request.js +4 -0
- package/packages/dd-trace/src/config/generated-config-types.d.ts +2 -0
- package/packages/dd-trace/src/config/helper.js +10 -2
- package/packages/dd-trace/src/config/index.js +20 -5
- package/packages/dd-trace/src/config/supported-configurations.json +10 -0
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +14 -7
- package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +14 -1
- package/packages/dd-trace/src/debugger/devtools_client/request-options.js +1 -6
- package/packages/dd-trace/src/evp_proxy/direct.js +2 -28
- package/packages/dd-trace/src/evp_proxy/discovery.js +3 -4
- package/packages/dd-trace/src/evp_proxy/path.js +12 -3
- package/packages/dd-trace/src/exporters/agentless/writer.js +3 -1
- package/packages/dd-trace/src/exporters/common/docker.js +4 -1
- package/packages/dd-trace/src/exporters/common/proxy.js +55 -0
- package/packages/dd-trace/src/exporters/common/request.js +11 -3
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/meter_provider.js +5 -4
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +106 -25
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +12 -1
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +8 -2
- package/packages/dd-trace/src/plugin_manager.js +9 -3
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/util/test.js +4 -1
- package/packages/dd-trace/src/priority_sampler.js +4 -0
- package/packages/dd-trace/src/sampling_rule.js +3 -1
- package/packages/dd-trace/src/span_processor.js +18 -3
- package/packages/dd-trace/src/telemetry/send-data.js +5 -4
- package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/utils.js +0 -10
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
// The content of this file is copied from the `import-in-the-middle` package with minor modifications (https://www.npmjs.com/package/import-in-the-middle)
|
|
4
|
-
const { pathToFileURL, fileURLToPath } = require('node:url')
|
|
5
4
|
const fs = require('node:fs')
|
|
6
5
|
const path = require('node:path')
|
|
7
|
-
const {
|
|
6
|
+
const { fileURLToPath, pathToFileURL } = require('node:url')
|
|
8
7
|
|
|
9
|
-
const
|
|
10
|
-
|
|
8
|
+
const { createEsmResolver, driveGetExportsGenerator } = require('./resolver')
|
|
9
|
+
|
|
10
|
+
/** @typedef {ReturnType<typeof createEsmResolver>} EsmResolver */
|
|
11
11
|
|
|
12
12
|
const getExportsImporting = (url) => import(url).then(Object.keys)
|
|
13
13
|
let getExportsModulePromise
|
|
@@ -19,16 +19,33 @@ const loadGetExportsModule = () => {
|
|
|
19
19
|
return getExportsModulePromise
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
/**
|
|
23
|
+
* @param {URL} srcUrl
|
|
24
|
+
* @param {object} context
|
|
25
|
+
* @param {(url: URL, context: object) => { source: string, format: string }} getSource
|
|
26
|
+
* @param {EsmResolver} resolver
|
|
27
|
+
* @returns {Promise<{
|
|
28
|
+
* exportNames: Iterable<string>,
|
|
29
|
+
* hasModuleExportsCJSDefault: boolean,
|
|
30
|
+
* starReexports?: Array<{ specifier: string, parentURL: string }>
|
|
31
|
+
* }>}
|
|
32
|
+
*/
|
|
33
|
+
const getExports = async (srcUrl, context, getSource, resolver) => {
|
|
34
|
+
const mod = await loadGetExportsModule()
|
|
35
|
+
const exportsGenerator = mod.getExports(srcUrl, context, getSource)
|
|
36
|
+
/**
|
|
37
|
+
* @param {string} specifier
|
|
38
|
+
* @param {{ parentURL: URL }} operationContext
|
|
39
|
+
* @returns {Promise<{ format: string, url: URL }>}
|
|
40
|
+
*/
|
|
41
|
+
const resolve = (specifier, operationContext) => resolveModule(specifier, operationContext, resolver)
|
|
42
|
+
const result = await driveGetExportsGenerator(exportsGenerator, getSource, resolve)
|
|
43
|
+
return {
|
|
44
|
+
exportNames: result.exportNames,
|
|
45
|
+
hasModuleExportsCJSDefault: mod.hasModuleExportsCJSDefault,
|
|
46
|
+
starReexports: result.starReexports,
|
|
30
47
|
}
|
|
31
|
-
|
|
48
|
+
}
|
|
32
49
|
|
|
33
50
|
function isStarExportLine (line) {
|
|
34
51
|
return /^\* from /.test(line)
|
|
@@ -58,82 +75,37 @@ function isBareSpecifier (specifier) {
|
|
|
58
75
|
}
|
|
59
76
|
}
|
|
60
77
|
|
|
61
|
-
function resolve (specifier, context) {
|
|
62
|
-
// This comes from an import, that is why import makes preference
|
|
63
|
-
const conditions = ['import']
|
|
64
|
-
|
|
65
|
-
if (specifier.startsWith('file://')) {
|
|
66
|
-
specifier = fileURLToPath(specifier)
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const resolved = require.resolve(specifier, {
|
|
70
|
-
paths: [fileURLToPath(context.parentURL)],
|
|
71
|
-
// @ts-expect-error - Node.js 22+ unofficially supports a conditions option
|
|
72
|
-
conditions,
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
return {
|
|
76
|
-
url: pathToFileURL(resolved),
|
|
77
|
-
format: isESMFile(resolved) ? 'module' : 'commonjs',
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function getSource (url, { format }) {
|
|
82
|
-
return {
|
|
83
|
-
source: fs.readFileSync(fileURLToPath(url), 'utf8'),
|
|
84
|
-
format,
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
78
|
/**
|
|
89
|
-
* @
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* @typedef {{ done: false, value: GetExportsOperation } | { done: true, value: Set<string> }} GetExportsResult
|
|
94
|
-
*/
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* @typedef {{
|
|
98
|
-
* next: (value?: unknown) => GetExportsResult,
|
|
99
|
-
* throw: (error?: unknown) => GetExportsResult,
|
|
100
|
-
* }} GetExportsGenerator
|
|
79
|
+
* @param {string} modulePath
|
|
80
|
+
* @param {string} format
|
|
81
|
+
* @returns {string}
|
|
101
82
|
*/
|
|
83
|
+
function getModuleFormat (modulePath, format) {
|
|
84
|
+
if (modulePath.endsWith('.mts')) return 'module-typescript'
|
|
85
|
+
if (modulePath.endsWith('.cts')) return 'commonjs-typescript'
|
|
86
|
+
if (modulePath.endsWith('.ts')) return `${format}-typescript`
|
|
87
|
+
return format
|
|
88
|
+
}
|
|
102
89
|
|
|
103
90
|
/**
|
|
104
|
-
*
|
|
91
|
+
* Resolves a module with the import conditions used by ESM instrumentation.
|
|
105
92
|
*
|
|
106
|
-
* @param {
|
|
107
|
-
* @param {
|
|
108
|
-
*
|
|
109
|
-
* @returns {
|
|
93
|
+
* @param {string} specifier
|
|
94
|
+
* @param {{ parentURL: URL }} context
|
|
95
|
+
* @param {EsmResolver} resolver
|
|
96
|
+
* @returns {Promise<{ format: string, url: URL }>}
|
|
110
97
|
*/
|
|
111
|
-
function
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
const operation = next.value
|
|
120
|
-
const operationType = operation[0]
|
|
121
|
-
|
|
122
|
-
if (operationType === LOAD_OPERATION) {
|
|
123
|
-
result = getSource(operation[1], operation[2])
|
|
124
|
-
} else if (operationType === RESOLVE_OPERATION) {
|
|
125
|
-
result = resolve(operation[1], operation[2])
|
|
126
|
-
} else {
|
|
127
|
-
throw new Error(`Unsupported import-in-the-middle getExports operation: ${operationType}`)
|
|
128
|
-
}
|
|
129
|
-
} catch (err) {
|
|
130
|
-
threw = true
|
|
131
|
-
error = err
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
next = threw ? exportsGenerator.throw(error) : exportsGenerator.next(result)
|
|
98
|
+
async function resolveModule (specifier, context, resolver) {
|
|
99
|
+
const url = new URL(await resolver.resolve(specifier, context.parentURL))
|
|
100
|
+
if (url.protocol === 'node:') return { format: 'builtin', url }
|
|
101
|
+
if (url.protocol !== 'file:') throw new Error(`Unsupported ESM resolution URL: ${url.href}`)
|
|
102
|
+
|
|
103
|
+
const resolved = fileURLToPath(url)
|
|
104
|
+
if (resolved.endsWith('.node')) {
|
|
105
|
+
throw new Error(`Unsupported ESM analysis target: ${resolved}`)
|
|
135
106
|
}
|
|
136
|
-
|
|
107
|
+
const format = isESMFile(resolved) ? 'module' : 'commonjs'
|
|
108
|
+
return { format, url }
|
|
137
109
|
}
|
|
138
110
|
|
|
139
111
|
/**
|
|
@@ -144,16 +116,72 @@ function driveGetExportsGenerator (exportsGenerator, getSource) {
|
|
|
144
116
|
* @param {boolean} [moduleData.internal]
|
|
145
117
|
* @param {object} moduleData.context
|
|
146
118
|
* @param {boolean} [moduleData.excludeDefault]
|
|
119
|
+
* @param {Map<string, string>} [moduleData.moduleSources]
|
|
120
|
+
* @param {EsmResolver} [moduleData.resolver]
|
|
121
|
+
* @param {(source: string, options: { loader: 'ts' }) => { code: string }} [moduleData.transform]
|
|
122
|
+
* @param {Set<string>} [activeModules]
|
|
147
123
|
* @returns {Promise<Map>}
|
|
148
124
|
*/
|
|
149
|
-
async function processModule (
|
|
150
|
-
|
|
125
|
+
async function processModule (
|
|
126
|
+
{ path, internal = false, context, excludeDefault = false, moduleSources = new Map(), resolver, transform },
|
|
127
|
+
activeModules
|
|
128
|
+
) {
|
|
129
|
+
const ownsResolver = resolver === undefined
|
|
130
|
+
resolver ??= createEsmResolver()
|
|
131
|
+
try {
|
|
132
|
+
return await processModuleWithResolver(
|
|
133
|
+
{ path, internal, context, excludeDefault, moduleSources, transform },
|
|
134
|
+
activeModules,
|
|
135
|
+
resolver
|
|
136
|
+
)
|
|
137
|
+
} finally {
|
|
138
|
+
if (ownsResolver) await resolver.close()
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* @param {object} moduleData
|
|
144
|
+
* @param {string} moduleData.path
|
|
145
|
+
* @param {boolean} moduleData.internal
|
|
146
|
+
* @param {object} moduleData.context
|
|
147
|
+
* @param {boolean} moduleData.excludeDefault
|
|
148
|
+
* @param {Map<string, string>} moduleData.moduleSources
|
|
149
|
+
* @param {(source: string, options: { loader: 'ts' }) => { code: string }} [moduleData.transform]
|
|
150
|
+
* @param {Set<string>} [activeModules]
|
|
151
|
+
* @param {EsmResolver} resolver
|
|
152
|
+
* @returns {Promise<Map>}
|
|
153
|
+
*/
|
|
154
|
+
async function processModuleWithResolver (
|
|
155
|
+
{ path, internal, context, excludeDefault, moduleSources, transform },
|
|
156
|
+
activeModules,
|
|
157
|
+
resolver
|
|
158
|
+
) {
|
|
159
|
+
let moduleExports, srcUrl
|
|
151
160
|
if (internal) {
|
|
152
161
|
// we can not read and parse of internal modules
|
|
153
|
-
|
|
162
|
+
moduleExports = { exportNames: await getExportsImporting(path) }
|
|
154
163
|
} else {
|
|
164
|
+
context = { ...context, format: getModuleFormat(path, context.format) }
|
|
155
165
|
srcUrl = pathToFileURL(path)
|
|
156
|
-
|
|
166
|
+
const readSource = (url) => {
|
|
167
|
+
const modulePath = fileURLToPath(url)
|
|
168
|
+
let source = moduleSources.get(modulePath)
|
|
169
|
+
if (source === undefined) {
|
|
170
|
+
source = fs.readFileSync(modulePath, 'utf8')
|
|
171
|
+
moduleSources.set(modulePath, source)
|
|
172
|
+
}
|
|
173
|
+
return source
|
|
174
|
+
}
|
|
175
|
+
const loadSource = (url, { format }) => {
|
|
176
|
+
let source = readSource(url)
|
|
177
|
+
if (transform && (format === 'module-typescript' || format === 'commonjs-typescript')) {
|
|
178
|
+
source = transform(source, { loader: 'ts' }).code
|
|
179
|
+
format = format === 'module-typescript' ? 'module' : 'commonjs'
|
|
180
|
+
}
|
|
181
|
+
return { format, source }
|
|
182
|
+
}
|
|
183
|
+
readSource(srcUrl)
|
|
184
|
+
moduleExports = await getExports(srcUrl, context, loadSource, resolver)
|
|
157
185
|
}
|
|
158
186
|
|
|
159
187
|
const starExports = new Set()
|
|
@@ -186,43 +214,31 @@ async function processModule ({ path, internal = false, context, excludeDefault
|
|
|
186
214
|
}
|
|
187
215
|
}
|
|
188
216
|
|
|
189
|
-
|
|
190
|
-
|
|
217
|
+
let starReexports = moduleExports.starReexports
|
|
218
|
+
for (const n of moduleExports.exportNames) {
|
|
219
|
+
const isDefault = n === 'default' || (
|
|
220
|
+
n === 'module.exports' &&
|
|
221
|
+
moduleExports.hasModuleExportsCJSDefault &&
|
|
222
|
+
(context.format === 'commonjs' || context.format === 'commonjs-typescript')
|
|
223
|
+
)
|
|
224
|
+
if (isDefault && excludeDefault) continue
|
|
191
225
|
|
|
192
226
|
if (isStarExportLine(n)) {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
const newSpecifier = isBareSpecifier(modFile) ? modFile : new URL(modFile, srcUrl).href
|
|
198
|
-
// We need to call `parentResolve` to resolve bare specifiers to a full
|
|
199
|
-
// URL. We also need to call `parentResolve` for all sub-modules to get
|
|
200
|
-
// the `format`. We can't rely on the parents `format` to know if this
|
|
201
|
-
// sub-module is ESM or CJS!
|
|
227
|
+
starReexports ??= []
|
|
228
|
+
starReexports.push({ parentURL: srcUrl.href, specifier: n.slice('* from '.length) })
|
|
229
|
+
continue
|
|
230
|
+
}
|
|
202
231
|
|
|
203
|
-
|
|
232
|
+
const variableName = `$dd${Buffer.from(n).toString('hex')}`
|
|
233
|
+
const objectKey = JSON.stringify(n)
|
|
234
|
+
const reExportedName = n === 'default' ? n : objectKey
|
|
204
235
|
|
|
205
|
-
|
|
206
|
-
const subSetters = await processModule({
|
|
207
|
-
path: fileURLToPath(result.url),
|
|
208
|
-
context: { ...context, format: result.format },
|
|
209
|
-
excludeDefault: true,
|
|
210
|
-
})
|
|
211
|
-
|
|
212
|
-
for (const [name, setter] of subSetters.entries()) {
|
|
213
|
-
addSetter(name, setter, true)
|
|
214
|
-
}
|
|
215
|
-
} else {
|
|
216
|
-
const variableName = `$${n.replaceAll(/[^a-zA-Z0-9_$]/g, '_')}`
|
|
217
|
-
const objectKey = JSON.stringify(n)
|
|
218
|
-
const reExportedName = n === 'default' ? n : objectKey
|
|
219
|
-
|
|
220
|
-
addSetter(n, `
|
|
236
|
+
addSetter(n, `
|
|
221
237
|
let ${variableName}
|
|
222
238
|
try {
|
|
223
239
|
${variableName} = _[${objectKey}] = namespace[${objectKey}]
|
|
224
|
-
} catch (
|
|
225
|
-
if (!(
|
|
240
|
+
} catch (error) {
|
|
241
|
+
if (!(error instanceof ReferenceError)) throw error
|
|
226
242
|
}
|
|
227
243
|
export { ${variableName} as ${reExportedName} }
|
|
228
244
|
set[${objectKey}] = (v) => {
|
|
@@ -231,6 +247,34 @@ async function processModule ({ path, internal = false, context, excludeDefault
|
|
|
231
247
|
}
|
|
232
248
|
get[${objectKey}] = () => ${variableName}
|
|
233
249
|
`)
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (starReexports) {
|
|
253
|
+
for (const { parentURL, specifier } of starReexports) {
|
|
254
|
+
const baseUrl = new URL(parentURL)
|
|
255
|
+
const resolvedSpecifier = isBareSpecifier(specifier) ? specifier : new URL(specifier, baseUrl).href
|
|
256
|
+
// The runtime's import conditions and the declaring module's URL own star-export resolution.
|
|
257
|
+
// eslint-disable-next-line no-await-in-loop
|
|
258
|
+
const result = await resolveModule(resolvedSpecifier, { parentURL: baseUrl }, resolver)
|
|
259
|
+
|
|
260
|
+
activeModules ??= new Set([srcUrl.href])
|
|
261
|
+
if (activeModules.has(result.url.href)) continue
|
|
262
|
+
activeModules.add(result.url.href)
|
|
263
|
+
|
|
264
|
+
// eslint-disable-next-line no-await-in-loop
|
|
265
|
+
const subSetters = await processModuleWithResolver({
|
|
266
|
+
path: result.format === 'builtin' ? result.url.href : fileURLToPath(result.url),
|
|
267
|
+
internal: result.format === 'builtin',
|
|
268
|
+
context: { ...context, format: result.format },
|
|
269
|
+
excludeDefault: true,
|
|
270
|
+
moduleSources,
|
|
271
|
+
transform,
|
|
272
|
+
}, activeModules, resolver)
|
|
273
|
+
activeModules.delete(result.url.href)
|
|
274
|
+
|
|
275
|
+
for (const [name, setter] of subSetters.entries()) {
|
|
276
|
+
addSetter(name, setter, true)
|
|
277
|
+
}
|
|
234
278
|
}
|
|
235
279
|
}
|
|
236
280
|
|
|
@@ -246,8 +290,8 @@ async function processModule ({ path, internal = false, context, excludeDefault
|
|
|
246
290
|
* @returns {boolean}
|
|
247
291
|
*/
|
|
248
292
|
function isESMFile (fullPathToModule, modulePackageJsonPath, packageJson = {}) {
|
|
249
|
-
if (fullPathToModule.endsWith('.mjs')) return true
|
|
250
|
-
if (fullPathToModule.endsWith('.cjs')) return false
|
|
293
|
+
if (fullPathToModule.endsWith('.mjs') || fullPathToModule.endsWith('.mts')) return true
|
|
294
|
+
if (fullPathToModule.endsWith('.cjs') || fullPathToModule.endsWith('.cts')) return false
|
|
251
295
|
|
|
252
296
|
const pathParts = fullPathToModule.split(path.sep)
|
|
253
297
|
do {
|
|
@@ -271,6 +315,6 @@ function isESMFile (fullPathToModule, modulePackageJsonPath, packageJson = {}) {
|
|
|
271
315
|
}
|
|
272
316
|
|
|
273
317
|
module.exports = {
|
|
274
|
-
processModule,
|
|
275
318
|
isESMFile,
|
|
319
|
+
processModule,
|
|
276
320
|
}
|
|
@@ -6,112 +6,48 @@ const { addHook, getHooks } = require('./helpers/instrument')
|
|
|
6
6
|
|
|
7
7
|
const vercelAiTracingChannel = tracingChannel('dd-trace:vercel-ai')
|
|
8
8
|
const vercelAiSpanSetAttributesChannel = channel('dd-trace:vercel-ai:span:setAttributes')
|
|
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
9
|
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
// Published synchronously per model call with the native call data. A subscriber may set either
|
|
11
|
+
// callback and this instrumentation applies it, so the wrapping stays here and only the policy
|
|
12
|
+
// lives outside:
|
|
13
|
+
// beforeResult () => Promise<void>|undefined holds the result back until it settles
|
|
14
|
+
// onResult (result) => unknown|Promise<unknown> inspects or replaces the delivered result
|
|
15
|
+
const modelInterceptChannel = channel('dd-trace:vercel-ai:model:intercept')
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
* Publishes a provider-native lifecycle payload to a cancelable lifecycle channel.
|
|
19
|
-
*
|
|
20
|
-
* Subscribers push async work into `pending` synchronously during publication and
|
|
21
|
-
* abort `abortController` with an error before the pushed promise resolves to block.
|
|
22
|
-
*
|
|
23
|
-
* @param {object} lifecycleChannel
|
|
24
|
-
* @param {object} payload
|
|
25
|
-
* @returns {Promise<void>}
|
|
26
|
-
*/
|
|
27
|
-
function publishLifecycle (lifecycleChannel, payload) {
|
|
28
|
-
const abortController = new AbortController()
|
|
29
|
-
const ctx = { ...payload, abortController, pending: [] }
|
|
17
|
+
const INTERCEPTED_MODEL_METHODS = ['doGenerate', 'doStream']
|
|
30
18
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return Promise.all(ctx.pending).then(() => {
|
|
34
|
-
if (abortController.signal.aborted) {
|
|
35
|
-
throw abortController.signal.reason
|
|
36
|
-
}
|
|
37
|
-
})
|
|
38
|
-
}
|
|
19
|
+
const tracers = new WeakSet()
|
|
20
|
+
const wrappedModels = new WeakSet()
|
|
39
21
|
|
|
40
22
|
/**
|
|
41
|
-
* Wraps a
|
|
23
|
+
* Wraps a language model's call methods so subscribers can interpose on each call.
|
|
42
24
|
*
|
|
43
|
-
* @param {object} model
|
|
25
|
+
* @param {object} model
|
|
44
26
|
*/
|
|
45
|
-
function
|
|
27
|
+
function wrapModel (model) {
|
|
46
28
|
if (!model || wrappedModels.has(model)) return
|
|
47
29
|
wrappedModels.add(model)
|
|
48
30
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
return function (options) {
|
|
52
|
-
const originalResult = original.call(this, options)
|
|
31
|
+
for (const method of INTERCEPTED_MODEL_METHODS) {
|
|
32
|
+
if (typeof model[method] !== 'function') continue
|
|
53
33
|
|
|
54
|
-
|
|
55
|
-
|
|
34
|
+
shimmer.wrap(model, method, original => function (...args) {
|
|
35
|
+
const result = original.apply(this, args)
|
|
36
|
+
if (!modelInterceptChannel.hasSubscribers) return result
|
|
56
37
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
: Promise.resolve()
|
|
38
|
+
const interceptCtx = { method, arguments: args }
|
|
39
|
+
modelInterceptChannel.publish(interceptCtx)
|
|
60
40
|
|
|
61
|
-
|
|
62
|
-
.then(([, result]) => {
|
|
63
|
-
if (!doGenerateAfterChannel.hasSubscribers || !result.content?.length) return result
|
|
64
|
-
const payload = { prompt: options.prompt, options, result }
|
|
65
|
-
return publishLifecycle(doGenerateAfterChannel, payload).then(() => result)
|
|
66
|
-
})
|
|
67
|
-
}
|
|
68
|
-
})
|
|
69
|
-
}
|
|
41
|
+
if (!interceptCtx.beforeResult && !interceptCtx.onResult) return result
|
|
70
42
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const beforeEvaluation = doStreamBeforeChannel.hasSubscribers
|
|
80
|
-
? publishLifecycle(doStreamBeforeChannel, { prompt: options.prompt, options })
|
|
81
|
-
: Promise.resolve()
|
|
82
|
-
|
|
83
|
-
return Promise.all([beforeEvaluation, originalResult])
|
|
84
|
-
.then(([, result]) => {
|
|
85
|
-
if (!doStreamAfterChannel.hasSubscribers) return result
|
|
86
|
-
|
|
87
|
-
const chunks = []
|
|
88
|
-
const reader = result.stream.getReader()
|
|
89
|
-
|
|
90
|
-
function readAll () {
|
|
91
|
-
return reader.read().then(({ done, value }) => {
|
|
92
|
-
if (done) return
|
|
93
|
-
chunks.push(value)
|
|
94
|
-
return readAll()
|
|
95
|
-
})
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return readAll().then(() => {
|
|
99
|
-
return publishLifecycle(doStreamAfterChannel, { prompt: options.prompt, options, chunks })
|
|
100
|
-
.then(() => {
|
|
101
|
-
// eslint-disable-next-line n/no-unsupported-features/node-builtins
|
|
102
|
-
const stream = new ReadableStream({
|
|
103
|
-
start (controller) {
|
|
104
|
-
for (const chunk of chunks) {
|
|
105
|
-
controller.enqueue(chunk)
|
|
106
|
-
}
|
|
107
|
-
controller.close()
|
|
108
|
-
},
|
|
109
|
-
})
|
|
110
|
-
return { ...result, stream }
|
|
111
|
-
})
|
|
112
|
-
})
|
|
113
|
-
})
|
|
114
|
-
}
|
|
43
|
+
// A model may return any thenable, so normalize before attaching handlers.
|
|
44
|
+
const settled = Promise.resolve(result)
|
|
45
|
+
// The interceptor's rejection wins over an earlier SDK rejection.
|
|
46
|
+
settled.catch(() => {})
|
|
47
|
+
|
|
48
|
+
return Promise.resolve(interceptCtx.beforeResult?.())
|
|
49
|
+
.then(() => settled)
|
|
50
|
+
.then(value => (interceptCtx.onResult ? interceptCtx.onResult(value) : value))
|
|
115
51
|
})
|
|
116
52
|
}
|
|
117
53
|
}
|
|
@@ -209,7 +145,9 @@ function wrapTracer (tracer) {
|
|
|
209
145
|
})
|
|
210
146
|
}
|
|
211
147
|
|
|
212
|
-
|
|
148
|
+
let orchestrionSubscribed = false
|
|
149
|
+
|
|
150
|
+
for (const hook of getHooks('ai').values()) {
|
|
213
151
|
if (hook.file === 'dist/index.js') {
|
|
214
152
|
// if not removed, the below hook will never match correctly
|
|
215
153
|
// however, it is still needed in the orchestrion definition
|
|
@@ -217,6 +155,9 @@ for (const hook of getHooks('ai')) {
|
|
|
217
155
|
}
|
|
218
156
|
|
|
219
157
|
addHook(hook, exports => {
|
|
158
|
+
if (orchestrionSubscribed) return exports
|
|
159
|
+
orchestrionSubscribed = true
|
|
160
|
+
|
|
220
161
|
const getTracerChannel = tracingChannel('orchestrion:ai:getTracer')
|
|
221
162
|
getTracerChannel.subscribe({
|
|
222
163
|
end (ctx) {
|
|
@@ -258,11 +199,14 @@ for (const hook of getHooks('ai')) {
|
|
|
258
199
|
tracingChannel('orchestrion:ai:resolveLanguageModel').subscribe({
|
|
259
200
|
end (ctx) {
|
|
260
201
|
const model = ctx.arguments[0]
|
|
202
|
+
|
|
203
|
+
// The SDK builds a model from a string id, in which case only the resolved instance is
|
|
204
|
+
// worth wrapping; when the caller passed an instance, that is the one the SDK calls.
|
|
261
205
|
if (typeof model !== 'string' && model !== ctx.result) {
|
|
262
|
-
|
|
206
|
+
wrapModel(model)
|
|
263
207
|
wrappedModels.add(ctx.result)
|
|
264
208
|
} else {
|
|
265
|
-
|
|
209
|
+
wrapModel(ctx.result)
|
|
266
210
|
}
|
|
267
211
|
},
|
|
268
212
|
})
|
|
@@ -315,5 +259,3 @@ addHook({ name: 'ai', versions: ['>=7.0.0'] }, exports => {
|
|
|
315
259
|
|
|
316
260
|
return exports
|
|
317
261
|
})
|
|
318
|
-
|
|
319
|
-
module.exports = { wrapModelWithLifecycle }
|