dd-trace 6.0.0 → 6.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE-3rdparty.csv +1 -2
- package/index.d.ts +21 -1
- package/package.json +32 -29
- package/packages/datadog-instrumentations/src/ai.js +45 -0
- package/packages/datadog-instrumentations/src/apollo-server.js +5 -13
- package/packages/datadog-instrumentations/src/child_process.js +3 -3
- package/packages/datadog-instrumentations/src/claude-agent-sdk.js +587 -0
- package/packages/datadog-instrumentations/src/cucumber.js +102 -43
- package/packages/datadog-instrumentations/src/cypress-config.js +11 -3
- package/packages/datadog-instrumentations/src/fastify.js +27 -8
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/instrument.js +7 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +6 -6
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/claude-agent-sdk.js +17 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
- package/packages/datadog-instrumentations/src/jest.js +713 -63
- package/packages/datadog-instrumentations/src/mocha/main.js +24 -3
- package/packages/datadog-instrumentations/src/mocha/utils.js +128 -22
- package/packages/datadog-instrumentations/src/mocha/worker.js +13 -0
- package/packages/datadog-instrumentations/src/mongodb.js +74 -0
- package/packages/datadog-instrumentations/src/mongoose.js +4 -5
- package/packages/datadog-instrumentations/src/playwright.js +13 -9
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +4 -0
- package/packages/datadog-instrumentations/src/vitest-main.js +28 -1
- package/packages/datadog-instrumentations/src/vitest-util.js +5 -0
- package/packages/datadog-instrumentations/src/vitest-worker.js +104 -34
- package/packages/datadog-plugin-ai/src/index.js +1 -1
- package/packages/datadog-plugin-ai/src/tracing.js +66 -3
- package/packages/datadog-plugin-ai/src/utils.js +17 -4
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/context.js +1 -0
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/handler.js +1 -0
- package/packages/datadog-plugin-child_process/src/index.js +13 -2
- package/packages/datadog-plugin-claude-agent-sdk/src/index.js +31 -0
- package/packages/datadog-plugin-claude-agent-sdk/src/tracing.js +107 -0
- package/packages/datadog-plugin-claude-agent-sdk/src/util.js +15 -0
- package/packages/datadog-plugin-cucumber/src/index.js +14 -23
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +351 -11
- package/packages/datadog-plugin-cypress/src/index.js +47 -2
- package/packages/datadog-plugin-cypress/src/plugin.js +1 -0
- package/packages/datadog-plugin-graphql/src/execute.js +21 -10
- package/packages/datadog-plugin-graphql/src/index.js +17 -1
- package/packages/datadog-plugin-graphql/src/utils.js +9 -2
- package/packages/datadog-plugin-graphql/src/validate.js +1 -1
- package/packages/datadog-plugin-jest/src/index.js +30 -14
- package/packages/datadog-plugin-mocha/src/index.js +39 -14
- package/packages/datadog-plugin-mongodb-core/src/bulk-write.js +43 -0
- package/packages/datadog-plugin-mongodb-core/src/index.js +8 -443
- package/packages/datadog-plugin-mongodb-core/src/query.js +452 -0
- package/packages/datadog-plugin-playwright/src/index.js +3 -2
- package/packages/datadog-plugin-vitest/src/index.js +51 -9
- package/packages/dd-trace/src/aiguard/index.js +9 -14
- package/packages/dd-trace/src/aiguard/integrations/index.js +34 -0
- package/packages/dd-trace/src/aiguard/integrations/openai.js +47 -33
- package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +42 -28
- package/packages/dd-trace/src/appsec/graphql.js +9 -6
- package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +3 -10
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +26 -1
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +115 -24
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +3 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +3 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +75 -7
- package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +156 -0
- package/packages/dd-trace/src/config/generated-config-types.d.ts +10 -0
- package/packages/dd-trace/src/config/supported-configurations.json +37 -0
- package/packages/dd-trace/src/crashtracking/crashtracker.js +2 -2
- package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +403 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/index.js +8 -395
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +27 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +363 -0
- package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/index.js +270 -0
- package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/utils.js +10 -0
- package/packages/dd-trace/src/opentelemetry/span.js +5 -0
- package/packages/dd-trace/src/plugin_manager.js +16 -0
- package/packages/dd-trace/src/plugins/ci_plugin.js +147 -7
- package/packages/dd-trace/src/plugins/index.js +2 -0
- package/packages/dd-trace/src/plugins/util/test.js +4 -0
- package/packages/dd-trace/src/profiling/exporters/agent.js +28 -3
|
@@ -1,452 +1,17 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const CompositePlugin = require('../../dd-trace/src/plugins/composite')
|
|
4
|
+
const MongodbCoreBulkWritePlugin = require('./bulk-write')
|
|
5
|
+
const MongodbCoreQueryPlugin = require('./query')
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class MongodbCorePlugin extends DatabasePlugin {
|
|
7
|
+
class MongodbCorePlugin extends CompositePlugin {
|
|
8
8
|
static id = 'mongodb-core'
|
|
9
|
-
static
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
* @override
|
|
14
|
-
*/
|
|
15
|
-
static peerServicePrecursors = []
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* @override
|
|
19
|
-
*/
|
|
20
|
-
configure (config) {
|
|
21
|
-
super.configure(config)
|
|
22
|
-
|
|
23
|
-
this.config.heartbeatEnabled = config.heartbeatEnabled ??
|
|
24
|
-
this._tracerConfig.DD_TRACE_MONGODB_HEARTBEAT_ENABLED
|
|
25
|
-
this.config.obfuscateQuery = normaliseObfuscateQuery(
|
|
26
|
-
config.obfuscateQuery ?? this._tracerConfig.DD_TRACE_MONGODB_OBFUSCATE_QUERY
|
|
27
|
-
)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
bindStart (ctx) {
|
|
31
|
-
const { ns, ops, options = {}, name } = ctx
|
|
32
|
-
// heartbeat commands can be disabled if this.config.heartbeatEnabled is false
|
|
33
|
-
if (!this.config.heartbeatEnabled && isHeartbeat(ops, this.config)) {
|
|
34
|
-
return
|
|
35
|
-
}
|
|
36
|
-
const query = getQuery(ops, this.config.obfuscateQuery)
|
|
37
|
-
const resource = truncate(getResource(this, ns, query, name))
|
|
38
|
-
const serviceResult = this.serviceName({ pluginConfig: this.config })
|
|
39
|
-
const span = this.startSpan(this.operationName(), {
|
|
40
|
-
service: serviceResult,
|
|
41
|
-
resource,
|
|
42
|
-
type: 'mongodb',
|
|
43
|
-
kind: 'client',
|
|
44
|
-
meta: {
|
|
45
|
-
// this is not technically correct since it includes the collection but we changing will break customer stuff
|
|
46
|
-
'db.name': ns,
|
|
47
|
-
'mongodb.query': query,
|
|
48
|
-
'out.host': options.host,
|
|
49
|
-
'out.port': options.port,
|
|
50
|
-
},
|
|
51
|
-
}, ctx)
|
|
52
|
-
const comment = this.injectDbmComment(span, ops.comment, serviceResult.name)
|
|
53
|
-
if (comment) {
|
|
54
|
-
ops.comment = comment
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return ctx.currentStore
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* @override
|
|
62
|
-
*/
|
|
63
|
-
getPeerService (tags) {
|
|
64
|
-
let ns = tags['db.name']
|
|
65
|
-
if (ns && tags['peer.service'] === undefined) {
|
|
66
|
-
const dotIndex = ns.indexOf('.')
|
|
67
|
-
if (dotIndex !== -1) {
|
|
68
|
-
ns = ns.slice(0, dotIndex)
|
|
69
|
-
}
|
|
70
|
-
// the mongo ns is either dbName either dbName.collection. So we keep the first part
|
|
71
|
-
tags['peer.service'] = ns
|
|
72
|
-
}
|
|
73
|
-
return super.getPeerService(tags)
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
injectDbmComment (span, comment, serviceName) {
|
|
77
|
-
const dbmTraceComment = this.createDbmComment(span, serviceName)
|
|
78
|
-
|
|
79
|
-
if (!dbmTraceComment) {
|
|
80
|
-
return comment
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (comment) {
|
|
84
|
-
// if the command already has a comment, append the dbm trace comment
|
|
85
|
-
if (typeof comment === 'string') {
|
|
86
|
-
comment += `,${dbmTraceComment}`
|
|
87
|
-
} else if (Array.isArray(comment)) {
|
|
88
|
-
comment.push(dbmTraceComment)
|
|
89
|
-
} // do nothing if the comment is not a string or an array
|
|
90
|
-
} else {
|
|
91
|
-
comment = dbmTraceComment
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return comment
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const MAX_DEPTH = 10
|
|
99
|
-
const MAX_QUERY_LENGTH = 10_000
|
|
100
|
-
|
|
101
|
-
function extractQuery (statements) {
|
|
102
|
-
if (statements.length === 1 && statements[0].q) return statements[0].q
|
|
103
|
-
|
|
104
|
-
const extractedQueries = []
|
|
105
|
-
for (let i = 0; i < statements.length; i++) {
|
|
106
|
-
if (statements[i].q) {
|
|
107
|
-
extractedQueries.push(statements[i].q)
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return extractedQueries
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* @param {Record<string, unknown> | unknown[] | undefined} cmd
|
|
116
|
-
* @param {'none' | 'types' | 'redact'} mode
|
|
117
|
-
*/
|
|
118
|
-
function getQuery (cmd, mode) {
|
|
119
|
-
if (!cmd || (typeof cmd !== 'object' && !Array.isArray(cmd))) return
|
|
120
|
-
|
|
121
|
-
if (Array.isArray(cmd)) return sanitiseAndStringify(extractQuery(cmd), mode)
|
|
122
|
-
if (cmd.query) return sanitiseAndStringify(cmd.query, mode)
|
|
123
|
-
if (cmd.filter) return sanitiseAndStringify(cmd.filter, mode)
|
|
124
|
-
if (cmd.pipeline) return sanitiseAndStringify(cmd.pipeline, mode)
|
|
125
|
-
if (cmd.deletes) return sanitiseAndStringify(extractQuery(cmd.deletes), mode)
|
|
126
|
-
if (cmd.updates) return sanitiseAndStringify(extractQuery(cmd.updates), mode)
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function getResource (plugin, ns, query, operationName) {
|
|
130
|
-
let resource = `${operationName} ${ns}`
|
|
131
|
-
|
|
132
|
-
if (plugin.config.queryInResourceName && query) {
|
|
133
|
-
resource += ` ${query}`
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return resource
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function truncate (input) {
|
|
140
|
-
return input.length > MAX_QUERY_LENGTH ? input.slice(0, MAX_QUERY_LENGTH) : input
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Depth doubles as the cycle bound: a cycle pushes past MAX_DEPTH and bails,
|
|
144
|
-
// after which the slow path catches it via its ancestor stack.
|
|
145
|
-
/** @param {unknown} input */
|
|
146
|
-
function canStringifyDirect (input) {
|
|
147
|
-
if (input === null ||
|
|
148
|
-
typeof input !== 'object' ||
|
|
149
|
-
ArrayBuffer.isView(input) ||
|
|
150
|
-
input._bsontype !== undefined ||
|
|
151
|
-
isRegExp(input) ||
|
|
152
|
-
isMap(input) ||
|
|
153
|
-
typeof input.toJSON === 'function') {
|
|
154
|
-
return false
|
|
155
|
-
}
|
|
156
|
-
return canStringifyDirectWalk(input, 1)
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* @param {Record<string, unknown> | unknown[]} value
|
|
161
|
-
* @param {number} depth
|
|
162
|
-
*/
|
|
163
|
-
function canStringifyDirectWalk (value, depth) {
|
|
164
|
-
if (depth > MAX_DEPTH) return false
|
|
165
|
-
const children = Array.isArray(value) ? value : Object.values(value)
|
|
166
|
-
for (const child of children) {
|
|
167
|
-
if (child === null ||
|
|
168
|
-
typeof child === 'string' ||
|
|
169
|
-
typeof child === 'number' ||
|
|
170
|
-
typeof child === 'boolean') {
|
|
171
|
-
continue
|
|
172
|
-
}
|
|
173
|
-
if (typeof child !== 'object' ||
|
|
174
|
-
ArrayBuffer.isView(child) ||
|
|
175
|
-
child._bsontype !== undefined ||
|
|
176
|
-
isRegExp(child) ||
|
|
177
|
-
isMap(child) ||
|
|
178
|
-
typeof child.toJSON === 'function') {
|
|
179
|
-
return false
|
|
180
|
-
}
|
|
181
|
-
if (!canStringifyDirectWalk(child, depth + 1)) return false
|
|
182
|
-
}
|
|
183
|
-
return true
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* @param {Record<string, unknown> | unknown[]} input
|
|
188
|
-
* @param {'none' | 'types' | 'redact'} mode
|
|
189
|
-
*/
|
|
190
|
-
function sanitiseAndStringify (input, mode) {
|
|
191
|
-
if (mode === 'none') {
|
|
192
|
-
if (canStringifyDirect(input)) return JSON.stringify(input)
|
|
193
|
-
return buildNone(input, [])
|
|
194
|
-
}
|
|
195
|
-
if (mode === 'redact') return buildRedact(input, [])
|
|
196
|
-
return buildTypes(input, [])
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
const REDACT_LEAF = '"?"'
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* @param {RegExp} value
|
|
203
|
-
* @returns {string}
|
|
204
|
-
*/
|
|
205
|
-
function stringifyRegExp (value) {
|
|
206
|
-
return `{"$regex":${JSON.stringify(value.source)},"$options":${JSON.stringify(value.flags)}}`
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* @param {Record<string, unknown> | unknown[]} value
|
|
211
|
-
* @param {object[]} ancestors
|
|
212
|
-
* @returns {string | undefined}
|
|
213
|
-
*/
|
|
214
|
-
function buildNone (value, ancestors) {
|
|
215
|
-
// ArrayBuffer views (Buffer, every TypedArray, DataView) and Binary BSON
|
|
216
|
-
// wrappers redact at the leaf; the walker neither recurses into the bytes
|
|
217
|
-
// nor invokes any custom conversion.
|
|
218
|
-
const bsontype = value._bsontype
|
|
219
|
-
if (ArrayBuffer.isView(value) || bsontype === 'Binary' ||
|
|
220
|
-
ancestors.length >= MAX_DEPTH || ancestors.includes(value)) {
|
|
221
|
-
return REDACT_LEAF
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
if (isRegExp(value)) return stringifyRegExp(value)
|
|
225
|
-
|
|
226
|
-
// Mirror JSON.stringify's contract: when `toJSON` is present, walk its
|
|
227
|
-
// result (wrappers like Timestamp / Decimal128 expand to a small object,
|
|
228
|
-
// ObjectId / Date flatten to a primitive).
|
|
229
|
-
if (typeof value.toJSON === 'function') {
|
|
230
|
-
const json = value.toJSON()
|
|
231
|
-
if (json === value) return REDACT_LEAF
|
|
232
|
-
// JSON.stringify keeps a null result as null (an invalid Date's toJSON
|
|
233
|
-
// returns null); only function / symbol / undefined results drop the key.
|
|
234
|
-
if (json === null) return 'null'
|
|
235
|
-
if (typeof json !== 'object') return classifyLeafForNone(json)
|
|
236
|
-
// A wrapper that exposes binary state through toJSON (Buffer-backed
|
|
237
|
-
// class with WeakMap state, etc.) returns a TypedArray here. Re-screen
|
|
238
|
-
// before the per-key walk would expand it element by element.
|
|
239
|
-
if (ArrayBuffer.isView(json) || json._bsontype === 'Binary') return REDACT_LEAF
|
|
240
|
-
value = json
|
|
241
|
-
} else if (bsontype !== undefined) {
|
|
242
|
-
return REDACT_LEAF
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// The driver serializes a Map via its entries; mirror that as a document so
|
|
246
|
-
// the tag matches the wire shape.
|
|
247
|
-
if (isMap(value)) value = Object.fromEntries(value)
|
|
248
|
-
|
|
249
|
-
ancestors.push(value)
|
|
250
|
-
|
|
251
|
-
let result
|
|
252
|
-
if (Array.isArray(value)) {
|
|
253
|
-
result = '['
|
|
254
|
-
let sep = ''
|
|
255
|
-
for (let i = 0; i < value.length; i++) {
|
|
256
|
-
// JSON.stringify renders unsupported leaves (function, symbol, undefined) as null in arrays.
|
|
257
|
-
result += sep + (classifyForNone(value[i], ancestors) ?? 'null')
|
|
258
|
-
sep = ','
|
|
9
|
+
static get plugins () {
|
|
10
|
+
return {
|
|
11
|
+
query: MongodbCoreQueryPlugin,
|
|
12
|
+
bulkWrite: MongodbCoreBulkWritePlugin,
|
|
259
13
|
}
|
|
260
|
-
result += ']'
|
|
261
|
-
} else {
|
|
262
|
-
result = '{'
|
|
263
|
-
let sep = ''
|
|
264
|
-
for (const key of Object.keys(value)) {
|
|
265
|
-
const childResult = classifyForNone(value[key], ancestors)
|
|
266
|
-
if (childResult === undefined) continue
|
|
267
|
-
result += sep + JSON.stringify(key) + ':' + childResult
|
|
268
|
-
sep = ','
|
|
269
|
-
}
|
|
270
|
-
result += '}'
|
|
271
14
|
}
|
|
272
|
-
ancestors.pop()
|
|
273
|
-
return result
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
/**
|
|
277
|
-
* @param {unknown} child
|
|
278
|
-
* @param {object[]} ancestors
|
|
279
|
-
* @returns {string | undefined}
|
|
280
|
-
*/
|
|
281
|
-
function classifyForNone (child, ancestors) {
|
|
282
|
-
if (typeof child !== 'object') return classifyLeafForNone(child)
|
|
283
|
-
if (child === null) return 'null'
|
|
284
|
-
return buildNone(child, ancestors)
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
/**
|
|
288
|
-
* @param {unknown} leaf
|
|
289
|
-
* @returns {string | undefined}
|
|
290
|
-
*/
|
|
291
|
-
function classifyLeafForNone (leaf) {
|
|
292
|
-
// Implicit `undefined` for function / symbol / undefined matches the
|
|
293
|
-
// contract callers rely on: JSON.stringify drops those property values
|
|
294
|
-
// inside objects and writes `null` in arrays.
|
|
295
|
-
switch (typeof leaf) {
|
|
296
|
-
case 'string': return JSON.stringify(leaf)
|
|
297
|
-
case 'number': return Number.isFinite(leaf) ? String(leaf) : 'null'
|
|
298
|
-
case 'boolean': return leaf ? 'true' : 'false'
|
|
299
|
-
case 'bigint': return `"${String(leaf)}"`
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
/**
|
|
304
|
-
* @param {Record<string, unknown> | unknown[]} value
|
|
305
|
-
* @param {object[]} ancestors
|
|
306
|
-
*/
|
|
307
|
-
function buildRedact (value, ancestors) {
|
|
308
|
-
const bsontype = value._bsontype
|
|
309
|
-
if (ArrayBuffer.isView(value) || bsontype === 'Binary' || isRegExp(value) ||
|
|
310
|
-
ancestors.length >= MAX_DEPTH || ancestors.includes(value)) {
|
|
311
|
-
return REDACT_LEAF
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
// Mirror JSON.stringify: when `toJSON` is present, walk its result (which
|
|
315
|
-
// wrappers like Timestamp / Decimal128 expand to `{$timestamp: "..."}` etc).
|
|
316
|
-
// A primitive, null, or self-reference collapses to the sentinel — master's
|
|
317
|
-
// `value === original` short-circuit.
|
|
318
|
-
if (typeof value.toJSON === 'function') {
|
|
319
|
-
const json = value.toJSON()
|
|
320
|
-
if (typeof json !== 'object' || json === null || json === value) return REDACT_LEAF
|
|
321
|
-
// Re-screen: toJSON can return a TypedArray or Binary BSON wrapper.
|
|
322
|
-
if (ArrayBuffer.isView(json) || json._bsontype === 'Binary') return REDACT_LEAF
|
|
323
|
-
value = json
|
|
324
|
-
} else if (bsontype !== undefined) {
|
|
325
|
-
return REDACT_LEAF
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
if (isMap(value)) value = Object.fromEntries(value)
|
|
329
|
-
|
|
330
|
-
ancestors.push(value)
|
|
331
|
-
|
|
332
|
-
let result
|
|
333
|
-
if (Array.isArray(value)) {
|
|
334
|
-
result = '['
|
|
335
|
-
let sep = ''
|
|
336
|
-
for (let i = 0; i < value.length; i++) {
|
|
337
|
-
result += sep + classifyForRedact(value[i], ancestors)
|
|
338
|
-
sep = ','
|
|
339
|
-
}
|
|
340
|
-
result += ']'
|
|
341
|
-
} else {
|
|
342
|
-
result = '{'
|
|
343
|
-
let sep = ''
|
|
344
|
-
for (const key of Object.keys(value)) {
|
|
345
|
-
result += sep + JSON.stringify(key) + ':' + classifyForRedact(value[key], ancestors)
|
|
346
|
-
sep = ','
|
|
347
|
-
}
|
|
348
|
-
result += '}'
|
|
349
|
-
}
|
|
350
|
-
ancestors.pop()
|
|
351
|
-
return result
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
/**
|
|
355
|
-
* @param {unknown} child
|
|
356
|
-
* @param {object[]} ancestors
|
|
357
|
-
*/
|
|
358
|
-
function classifyForRedact (child, ancestors) {
|
|
359
|
-
if (typeof child !== 'object' || child === null) return REDACT_LEAF
|
|
360
|
-
return buildRedact(child, ancestors)
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
const TYPE_OBJECT = '"object"'
|
|
364
|
-
const TYPE_NULL = '"null"'
|
|
365
|
-
const TYPE_BY_TYPEOF = {
|
|
366
|
-
string: '"string"',
|
|
367
|
-
number: '"number"',
|
|
368
|
-
boolean: '"boolean"',
|
|
369
|
-
bigint: '"bigint"',
|
|
370
|
-
undefined: '"undefined"',
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
/**
|
|
374
|
-
* @param {Record<string, unknown> | unknown[]} value
|
|
375
|
-
* @param {object[]} ancestors
|
|
376
|
-
*/
|
|
377
|
-
function buildTypes (value, ancestors) {
|
|
378
|
-
const bsontype = value._bsontype
|
|
379
|
-
if (ArrayBuffer.isView(value) || bsontype === 'Binary' || isRegExp(value) ||
|
|
380
|
-
ancestors.length >= MAX_DEPTH || ancestors.includes(value)) {
|
|
381
|
-
return TYPE_OBJECT
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
if (typeof value.toJSON === 'function') {
|
|
385
|
-
const json = value.toJSON()
|
|
386
|
-
if (typeof json !== 'object' ||
|
|
387
|
-
json === null ||
|
|
388
|
-
json === value ||
|
|
389
|
-
ArrayBuffer.isView(json) ||
|
|
390
|
-
json._bsontype === 'Binary') {
|
|
391
|
-
return TYPE_OBJECT
|
|
392
|
-
}
|
|
393
|
-
value = json
|
|
394
|
-
} else if (bsontype !== undefined) {
|
|
395
|
-
return TYPE_OBJECT
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
if (isMap(value)) value = Object.fromEntries(value)
|
|
399
|
-
|
|
400
|
-
ancestors.push(value)
|
|
401
|
-
|
|
402
|
-
let result
|
|
403
|
-
if (Array.isArray(value)) {
|
|
404
|
-
result = '['
|
|
405
|
-
let sep = ''
|
|
406
|
-
for (let i = 0; i < value.length; i++) {
|
|
407
|
-
// JSON.stringify renders unsupported leaves (function, symbol) as null in arrays.
|
|
408
|
-
result += sep + (classifyForTypes(value[i], ancestors) ?? 'null')
|
|
409
|
-
sep = ','
|
|
410
|
-
}
|
|
411
|
-
result += ']'
|
|
412
|
-
} else {
|
|
413
|
-
result = '{'
|
|
414
|
-
let sep = ''
|
|
415
|
-
for (const key of Object.keys(value)) {
|
|
416
|
-
const childResult = classifyForTypes(value[key], ancestors)
|
|
417
|
-
if (childResult === undefined) continue
|
|
418
|
-
result += sep + JSON.stringify(key) + ':' + childResult
|
|
419
|
-
sep = ','
|
|
420
|
-
}
|
|
421
|
-
result += '}'
|
|
422
|
-
}
|
|
423
|
-
ancestors.pop()
|
|
424
|
-
return result
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
/**
|
|
428
|
-
* @param {unknown} child
|
|
429
|
-
* @param {object[]} ancestors
|
|
430
|
-
*/
|
|
431
|
-
function classifyForTypes (child, ancestors) {
|
|
432
|
-
if (typeof child !== 'object') return TYPE_BY_TYPEOF[typeof child]
|
|
433
|
-
if (child === null) return TYPE_NULL
|
|
434
|
-
return buildTypes(child, ancestors)
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
/** @param {unknown} value */
|
|
438
|
-
function normaliseObfuscateQuery (value) {
|
|
439
|
-
if (value === 'types' || value === 'redact') return value
|
|
440
|
-
return 'none'
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
function isHeartbeat (ops, config) {
|
|
444
|
-
// Check if it's a heartbeat command https://github.com/mongodb/specifications/blob/master/source/mongodb-handshake/handshake.md
|
|
445
|
-
return (
|
|
446
|
-
ops &&
|
|
447
|
-
typeof ops === 'object' &&
|
|
448
|
-
(ops.hello === 1 || ops.helloOk === true || ops.ismaster === 1 || ops.isMaster === 1)
|
|
449
|
-
)
|
|
450
15
|
}
|
|
451
16
|
|
|
452
17
|
module.exports = MongodbCorePlugin
|