dd-trace 5.68.0 → 5.70.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.
Files changed (48) hide show
  1. package/LICENSE-3rdparty.csv +6 -1
  2. package/index.d.ts +193 -1
  3. package/package.json +11 -2
  4. package/packages/datadog-core/src/storage.js +14 -13
  5. package/packages/datadog-esbuild/index.js +62 -26
  6. package/packages/datadog-instrumentations/src/helpers/instrument.js +4 -3
  7. package/packages/datadog-instrumentations/src/jest.js +86 -84
  8. package/packages/datadog-instrumentations/src/mocha/utils.js +5 -21
  9. package/packages/datadog-instrumentations/src/playwright.js +5 -2
  10. package/packages/datadog-plugin-confluentinc-kafka-javascript/src/index.js +6 -0
  11. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +0 -1
  12. package/packages/datadog-plugin-mongodb-core/src/index.js +18 -5
  13. package/packages/dd-trace/src/aiguard/client.js +25 -0
  14. package/packages/dd-trace/src/aiguard/noop.js +9 -0
  15. package/packages/dd-trace/src/aiguard/sdk.js +173 -0
  16. package/packages/dd-trace/src/aiguard/tags.js +11 -0
  17. package/packages/dd-trace/src/appsec/iast/path-line.js +21 -4
  18. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +6 -3
  19. package/packages/dd-trace/src/appsec/stack_trace.js +20 -1
  20. package/packages/dd-trace/src/config-helper.js +8 -1
  21. package/packages/dd-trace/src/config.js +23 -0
  22. package/packages/dd-trace/src/config_defaults.js +5 -0
  23. package/packages/dd-trace/src/datastreams/index.js +23 -1
  24. package/packages/dd-trace/src/llmobs/plugins/ai/index.js +11 -1
  25. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +8 -6
  26. package/packages/dd-trace/src/log/index.js +4 -4
  27. package/packages/dd-trace/src/noop/proxy.js +4 -0
  28. package/packages/dd-trace/src/opentracing/span.js +1 -1
  29. package/packages/dd-trace/src/payload-tagging/config/index.js +16 -0
  30. package/packages/dd-trace/src/payload-tagging/index.js +26 -15
  31. package/packages/dd-trace/src/payload-tagging/tagging.js +17 -8
  32. package/packages/dd-trace/src/pkg.js +3 -1
  33. package/packages/dd-trace/src/plugins/composite.js +3 -0
  34. package/packages/dd-trace/src/plugins/plugin.js +67 -0
  35. package/packages/dd-trace/src/plugins/util/git.js +1 -1
  36. package/packages/dd-trace/src/plugins/util/test.js +19 -30
  37. package/packages/dd-trace/src/priority_sampler.js +70 -46
  38. package/packages/dd-trace/src/proxy.js +15 -0
  39. package/packages/dd-trace/src/rate_limiter.js +26 -1
  40. package/packages/dd-trace/src/sampling_rule.js +124 -2
  41. package/packages/dd-trace/src/span_sampler.js +19 -0
  42. package/packages/dd-trace/src/standalone/product.js +9 -0
  43. package/packages/dd-trace/src/standalone/tracesource.js +16 -1
  44. package/packages/dd-trace/src/standalone/tracesource_priority_sampler.js +13 -0
  45. package/packages/dd-trace/src/startup-log.js +19 -1
  46. package/packages/dd-trace/src/supported-configurations.json +6 -0
  47. package/packages/dd-trace/src/util.js +1 -1
  48. package/version.js +4 -2
@@ -14,8 +14,8 @@ const { tagsFromObject } = require('./tagging')
14
14
  /**
15
15
  * Given an identified value, attempt to parse it as JSON if relevant
16
16
  *
17
- * @param {any} value
18
- * @returns {any} the parsed object if parsing was successful, the input if not
17
+ * @param {unknown} value
18
+ * @returns {unknown} the parsed object if parsing was successful, the input if not
19
19
  */
20
20
  function maybeJSONParseValue (value) {
21
21
  if (typeof value !== 'string' || value[0] !== '{') {
@@ -32,8 +32,8 @@ function maybeJSONParseValue (value) {
32
32
  /**
33
33
  * Apply expansion to all expansion JSONPath queries
34
34
  *
35
- * @param {Object} object
36
- * @param {[String]} expansionRules list of JSONPath queries
35
+ * @param {Record<string, unknown>} object
36
+ * @param {string[]} expansionRules list of JSONPath queries
37
37
  */
38
38
  function expand (object, expansionRules) {
39
39
  for (const rule of expansionRules) {
@@ -46,8 +46,8 @@ function expand (object, expansionRules) {
46
46
  /**
47
47
  * Apply redaction to all redaction JSONPath queries
48
48
  *
49
- * @param {Object} object
50
- * @param {[String]} redactionRules
49
+ * @param {Record<string, unknown>} object
50
+ * @param {string[]} redactionRules
51
51
  */
52
52
  function redact (object, redactionRules) {
53
53
  for (const rule of redactionRules) {
@@ -65,15 +65,10 @@ function redact (object, redactionRules) {
65
65
  * as there are leaf values in the object
66
66
  * This function performs side-effects on a _copy_ of the input object.
67
67
  *
68
- * @param {Object} config sdk configuration for the service
69
- * @param {[String]} config.expand expansion rules for the service
70
- * @param {[String]} config.request redaction rules for the request
71
- * @param {[String]} config.response redaction rules for the response
72
- * @param {Object} object the input object to generate tags from
73
- * @param {Object} opts tag generation options
74
- * @param {String} opts.prefix prefix for all generated tags
75
- * @param {number} opts.maxDepth maximum depth to traverse the object
76
- * @returns
68
+ * @param {{ expand: string[], request: string[], response: string[] }} config sdk configuration for the service
69
+ * @param {Record<string, unknown>} object the input object to generate tags from
70
+ * @param {{ prefix: string, maxDepth: number }} opts tag generation options
71
+ * @returns {Record<string, string|boolean>} Tags map
77
72
  */
78
73
  function computeTags (config, object, opts) {
79
74
  const payload = rfdc(object)
@@ -84,10 +79,26 @@ function computeTags (config, object, opts) {
84
79
  return tagsFromObject(payload, opts)
85
80
  }
86
81
 
82
+ /**
83
+ * Compute request tags with the request prefix.
84
+ *
85
+ * @param {{ expand: string[], request: string[], response: string[] }} config
86
+ * @param {Record<string, unknown>} object
87
+ * @param {{ maxDepth: number }} opts
88
+ * @returns {Record<string, string|boolean>}
89
+ */
87
90
  function tagsFromRequest (config, object, opts) {
88
91
  return computeTags(config, object, { ...opts, prefix: PAYLOAD_TAG_REQUEST_PREFIX })
89
92
  }
90
93
 
94
+ /**
95
+ * Compute response tags with the response prefix.
96
+ *
97
+ * @param {{ expand: string[], request: string[], response: string[] }} config
98
+ * @param {Record<string, unknown>} object
99
+ * @param {{ maxDepth: number }} opts
100
+ * @returns {Record<string, string|boolean>}
101
+ */
91
102
  function tagsFromResponse (config, object, opts) {
92
103
  return computeTags(config, object, { ...opts, prefix: PAYLOAD_TAG_RESPONSE_PREFIX })
93
104
  }
@@ -8,24 +8,33 @@ const redactedKeys = new Set([
8
8
  const truncated = 'truncated'
9
9
  const redacted = 'redacted'
10
10
 
11
+ /**
12
+ * Escapes dots in keys to preserve hierarchy in flattened tag names.
13
+ *
14
+ * @param {string} key
15
+ * @returns {string}
16
+ */
11
17
  function escapeKey (key) {
12
18
  return key.replaceAll('.', String.raw`\.`)
13
19
  }
14
20
 
15
21
  /**
16
- * Compute normalized payload tags from any given object.
17
- *
18
- * @param {object} object
19
- * @param {import('./mask').Mask} mask
20
- * @param {number} maxDepth
21
- * @param {string} prefix
22
- * @returns
23
- */
22
+ * Compute normalized payload tags from any given object.
23
+ *
24
+ * - Limits total tag count to `PAYLOAD_TAGGING_MAX_TAGS - 1` plus the `_dd.payload_tags_incomplete` flag
25
+ * - Truncates values at max depth and for large scalars
26
+ * - Redacts known sensitive keys
27
+ *
28
+ * @param {unknown} object - Input to flatten into tags
29
+ * @param {{ maxDepth: number, prefix: string }} opts - Traversal options
30
+ * @returns {Record<string, string|boolean>} Map of tag names to values
31
+ */
24
32
  function tagsFromObject (object, opts) {
25
33
  const { maxDepth, prefix } = opts
26
34
 
27
35
  let tagCount = 0
28
36
  let abort = false
37
+ /** @type {Record<string, string|boolean>} */
29
38
  const result = {}
30
39
 
31
40
  function tagRec (prefix, object, depth = 0) {
@@ -20,8 +20,10 @@ function findPkg () {
20
20
 
21
21
  const filePath = findUp('package.json', root, directory)
22
22
 
23
+ if (filePath === undefined) return {}
24
+
23
25
  try {
24
- return JSON.parse(fs.readFileSync(filePath, 'utf8'))
26
+ return require(filePath)
25
27
  } catch {
26
28
  return {}
27
29
  }
@@ -11,6 +11,9 @@ class CompositePlugin extends Plugin {
11
11
  }
12
12
  }
13
13
 
14
+ /**
15
+ * @override
16
+ */
14
17
  configure (config) {
15
18
  super.configure(config)
16
19
  for (const name in this.constructor.plugins) {
@@ -6,6 +6,24 @@ const dc = require('dc-polyfill')
6
6
  const logger = require('../log')
7
7
  const { storage } = require('../../../datadog-core')
8
8
 
9
+ /**
10
+ * Base class for all Datadog plugins.
11
+ *
12
+ * Subclasses MUST define a static field `id` with the integration identifier
13
+ * used across channels, span names, tags and telemetry.
14
+ *
15
+ * Example:
16
+ * ```js
17
+ * class MyPlugin extends Plugin {
18
+ * static id = 'myframework'
19
+ * }
20
+ * ```
21
+ *
22
+ * Notes about the tracer instance:
23
+ * - In some contexts the tracer may be wrapped and available as `{ _tracer: Tracer }`.
24
+ * Use the `tracer` getter which normalizes access.
25
+ */
26
+
9
27
  class Subscription {
10
28
  constructor (event, handler) {
11
29
  this._channel = dc.channel(event)
@@ -50,6 +68,12 @@ class StoreBinding {
50
68
  }
51
69
 
52
70
  module.exports = class Plugin {
71
+ /**
72
+ * Create a new plugin instance.
73
+ *
74
+ * @param {object} tracer Tracer instance or wrapper containing it under `_tracer`.
75
+ * @param {object} tracerConfig Global tracer configuration object.
76
+ */
53
77
  constructor (tracer, tracerConfig) {
54
78
  this._subscriptions = []
55
79
  this._bindings = []
@@ -59,10 +83,22 @@ module.exports = class Plugin {
59
83
  this._tracerConfig = tracerConfig // global tracer configuration
60
84
  }
61
85
 
86
+ /**
87
+ * Normalized tracer access. Returns the underlying tracer even if wrapped.
88
+ *
89
+ * @returns {object}
90
+ */
62
91
  get tracer () {
63
92
  return this._tracer?._tracer || this._tracer
64
93
  }
65
94
 
95
+ /**
96
+ * Enter a context with the provided span bound in storage.
97
+ *
98
+ * @param {object} span The span to bind as current.
99
+ * @param {object=} store Optional existing store to extend; if omitted, uses current store.
100
+ * @returns {void}
101
+ */
66
102
  enter (span, store) {
67
103
  store = store || storage('legacy').getStore()
68
104
  storage('legacy').enterWith({ ...store, span })
@@ -74,8 +110,19 @@ module.exports = class Plugin {
74
110
  storage('legacy').enterWith({ noop: true })
75
111
  }
76
112
 
113
+ /**
114
+ * Subscribe to a diagnostic channel with automatic error handling and enable/disable lifecycle.
115
+ *
116
+ * @param {string} channelName Diagnostic channel name.
117
+ * @param {(...args: unknown[]) => unknown} handler Handler invoked on messages.
118
+ * @returns {void}
119
+ */
77
120
  addSub (channelName, handler) {
78
121
  const plugin = this
122
+ /**
123
+ * @this {unknown}
124
+ * @returns {unknown}
125
+ */
79
126
  const wrappedHandler = function () {
80
127
  try {
81
128
  return handler.apply(this, arguments)
@@ -88,10 +135,23 @@ module.exports = class Plugin {
88
135
  this._subscriptions.push(new Subscription(channelName, wrappedHandler))
89
136
  }
90
137
 
138
+ /**
139
+ * Bind the tracer store to a diagnostic channel with a transform function.
140
+ *
141
+ * @param {string} channelName Diagnostic channel name.
142
+ * @param {(data: unknown) => object} transform Transform to compute the bound store.
143
+ * @returns {void}
144
+ */
91
145
  addBind (channelName, transform) {
92
146
  this._bindings.push(new StoreBinding(channelName, transform))
93
147
  }
94
148
 
149
+ /**
150
+ * Attach an error to the current active span (if any).
151
+ *
152
+ * @param {unknown} error Error object or sentinel value.
153
+ * @returns {void}
154
+ */
95
155
  addError (error) {
96
156
  const store = storage('legacy').getStore()
97
157
 
@@ -102,6 +162,13 @@ module.exports = class Plugin {
102
162
  }
103
163
  }
104
164
 
165
+ /**
166
+ * Enable or disable the plugin and (re)apply its configuration.
167
+ *
168
+ * @param {boolean|object} config Either a boolean to enable/disable or a configuration object
169
+ * containing at least `{ enabled: boolean }`.
170
+ * @returns {void}
171
+ */
105
172
  configure (config) {
106
173
  if (typeof config === 'boolean') {
107
174
  config = { enabled: config }
@@ -116,7 +116,7 @@ function isShallowRepository () {
116
116
 
117
117
  function getGitVersion () {
118
118
  const gitVersionString = sanitizedExec('git', ['version'])
119
- const gitVersionMatches = gitVersionString.match(/git version (\d+)\.(\d+)\.(\d+)/)
119
+ const gitVersionMatches = /** @type {RegExpMatchArray} */ (gitVersionString.match(/git version (\d+)\.(\d+)\.(\d+)/))
120
120
  try {
121
121
  return {
122
122
  major: Number.parseInt(gitVersionMatches[1]),
@@ -49,6 +49,13 @@ const { SAMPLING_RULE_DECISION } = require('../../constants')
49
49
  const { AUTO_KEEP } = require('../../../../../ext/priority')
50
50
  const { version: ddTraceVersion } = require('../../../../../package.json')
51
51
 
52
+ /**
53
+ * JSDoc types for test environment metadata helpers.
54
+ *
55
+ * @typedef {{ service?: string, isServiceUserProvided?: boolean }} TestEnvironmentConfig
56
+ * @typedef {Record<string, string|number|undefined>} TestEnvironmentMetadata
57
+ */
58
+
52
59
  // session tags
53
60
  const TEST_SESSION_NAME = 'test_session.name'
54
61
 
@@ -123,10 +130,6 @@ const PLAYWRIGHT_WORKER_TRACE_PAYLOAD_CODE = 90
123
130
  const VITEST_WORKER_TRACE_PAYLOAD_CODE = 100
124
131
  const VITEST_WORKER_LOGS_PAYLOAD_CODE = 102
125
132
 
126
- // Early flake detection util strings
127
- const EFD_STRING = "Retried by Datadog's Early Flake Detection"
128
- const EFD_TEST_NAME_REGEX = new RegExp(EFD_STRING + String.raw` \(#\d+\): `, 'g')
129
-
130
133
  // Library Capabilities Tagging
131
134
  const DD_CAPABILITIES_TEST_IMPACT_ANALYSIS = '_dd.library_capabilities.test_impact_analysis'
132
135
  const DD_CAPABILITIES_EARLY_FLAKE_DETECTION = '_dd.library_capabilities.early_flake_detection'
@@ -189,10 +192,6 @@ const TEST_MANAGEMENT_IS_QUARANTINED = 'test.test_management.is_quarantined'
189
192
  const TEST_MANAGEMENT_ENABLED = 'test.test_management.enabled'
190
193
  const TEST_MANAGEMENT_ATTEMPT_TO_FIX_PASSED = 'test.test_management.attempt_to_fix_passed'
191
194
 
192
- // Test Management utils strings
193
- const ATTEMPT_TO_FIX_STRING = "Retried by Datadog's Test Management"
194
- const ATTEMPT_TEST_NAME_REGEX = new RegExp(ATTEMPT_TO_FIX_STRING + String.raw` \(#\d+\): `, 'g')
195
-
196
195
  // Impacted tests
197
196
  const POSSIBLE_BASE_BRANCHES = ['main', 'master', 'preprod', 'prod', 'dev', 'development', 'trunk']
198
197
  const BASE_LIKE_BRANCH_FILTER = /^(main|master|preprod|prod|dev|development|trunk|release\/.*|hotfix\/.*)$/
@@ -268,12 +267,6 @@ module.exports = {
268
267
  getTestEndLine,
269
268
  removeInvalidMetadata,
270
269
  parseAnnotations,
271
- EFD_STRING,
272
- EFD_TEST_NAME_REGEX,
273
- removeEfdStringFromTestName,
274
- removeAttemptToFixStringFromTestName,
275
- addEfdStringToTestName,
276
- addAttemptToFixStringToTestName,
277
270
  getIsFaultyEarlyFlakeDetection,
278
271
  TEST_BROWSER_DRIVER,
279
272
  TEST_BROWSER_DRIVER_VERSION,
@@ -331,6 +324,10 @@ function validateUrl (url) {
331
324
  }
332
325
  }
333
326
 
327
+ /**
328
+ * @param {TestEnvironmentMetadata} metadata
329
+ * @returns {TestEnvironmentMetadata}
330
+ */
334
331
  function removeInvalidMetadata (metadata) {
335
332
  return Object.keys(metadata).reduce((filteredTags, tag) => {
336
333
  if (tag === GIT_REPOSITORY_URL && !validateGitRepositoryUrl(metadata[GIT_REPOSITORY_URL])) {
@@ -444,6 +441,13 @@ function checkShaDiscrepancies (ciMetadata, userProvidedGitMetadata) {
444
441
  )
445
442
  }
446
443
 
444
+ /**
445
+ * Build environment metadata for tests by merging CI, Git, runtime/OS and user-provided metadata.
446
+ *
447
+ * @param {string=} testFramework
448
+ * @param {TestEnvironmentConfig=} config
449
+ * @returns {TestEnvironmentMetadata}
450
+ */
447
451
  function getTestEnvironmentMetadata (testFramework, config, shouldSkipGitMetadataExtraction = false) {
448
452
  const ciMetadata = getCIMetadata()
449
453
  const userProvidedGitMetadata = getUserProviderGitMetadata()
@@ -479,6 +483,7 @@ function getTestEnvironmentMetadata (testFramework, config, shouldSkipGitMetadat
479
483
  })
480
484
  }
481
485
 
486
+ /** @type {TestEnvironmentMetadata} */
482
487
  const runtimeAndOSMetadata = getRuntimeAndOSMetadata()
483
488
 
484
489
  const metadata = {
@@ -818,22 +823,6 @@ function parseAnnotations (annotations) {
818
823
  }, {})
819
824
  }
820
825
 
821
- function addEfdStringToTestName (testName, numAttempt) {
822
- return `${EFD_STRING} (#${numAttempt}): ${testName}`
823
- }
824
-
825
- function addAttemptToFixStringToTestName (testName, numAttempt) {
826
- return `${ATTEMPT_TO_FIX_STRING} (#${numAttempt}): ${testName}`
827
- }
828
-
829
- function removeEfdStringFromTestName (testName) {
830
- return testName.replaceAll(EFD_TEST_NAME_REGEX, '')
831
- }
832
-
833
- function removeAttemptToFixStringFromTestName (testName) {
834
- return testName.replaceAll(ATTEMPT_TEST_NAME_REGEX, '')
835
- }
836
-
837
826
  function getIsFaultyEarlyFlakeDetection (projectSuites, testsBySuiteName, faultyThresholdPercentage) {
838
827
  let newSuites = 0
839
828
  for (const suite of projectSuites) {
@@ -46,8 +46,9 @@ const defaultSampler = new Sampler(AUTO_KEEP)
46
46
  * @class PrioritySampler
47
47
  * @typedef {import('./opentracing/span')} DatadogSpan
48
48
  * @typedef {import('./opentracing/span_context')} DatadogSpanContext
49
- * @typedef {import('./standalone/product')} PRODUCTS
49
+ * @typedef {{ id: number, mechanism?: number }} Product
50
50
  * @typedef {2|-1|1|0} SamplingPriority Empirically defined sampling priorities.
51
+ * @typedef {import('./sampling_rule')|Record<string, unknown>} SamplingRuleLike
51
52
  */
52
53
  class PrioritySampler {
53
54
  /**
@@ -55,12 +56,12 @@ class PrioritySampler {
55
56
  *
56
57
  * @typedef {Object} SamplingConfig
57
58
  * @property {number} [sampleRate] - The default sample rate for traces.
58
- * @property {string} [provenance] - The provenance of the sampling rule (e.g., "customer", "dynamic").
59
+ * @property {string} [provenance] - Optional rule provenance ("customer" or "dynamic").
59
60
  * @property {number} [rateLimit=100] - The maximum number of traces to sample per second.
60
- * @property {Array<SamplingRule>} [rules=[]] - An array of sampling rules to apply.
61
+ * @property {Array<import('./sampling_rule')>|Array<Record<string, unknown>>} [rules=[]] - Sampling rules or configs.
61
62
  *
62
63
  * @param {string} env - The environment name (e.g., "production", "staging").
63
- * @param {SamplingConfig} config - The configuration object for sampling.
64
+ * @param {SamplingConfig} [config] - The configuration object for sampling.
64
65
  */
65
66
  constructor (env, config) {
66
67
  this.configure(env, config)
@@ -69,22 +70,22 @@ class PrioritySampler {
69
70
 
70
71
  /**
71
72
  *
72
- * @param env {string}
73
- * @param opts {SamplingConfig}
73
+ * @param {string} env
74
+ * @param {SamplingConfig} config
74
75
  */
75
- configure (env, opts = {}) {
76
- const { sampleRate, provenance, rateLimit = 100, rules } = opts
76
+ configure (env, config = {}) {
77
+ const { sampleRate, provenance, rateLimit = 100, rules } = config
77
78
  this._env = env
78
79
  this._rules = this.#normalizeRules(rules || [], sampleRate, rateLimit, provenance)
79
80
  this._limiter = new RateLimiter(rateLimit)
80
81
 
81
- log.trace(env, opts)
82
+ log.trace(env, config)
82
83
  setSamplingRules(this._rules)
83
84
  }
84
85
 
85
86
  /**
86
- * @param span {DatadogSpan}
87
- * @returns {boolean}
87
+ * @param {DatadogSpan} span
88
+ * @returns {boolean} True if the trace should be sampled based on priority.
88
89
  */
89
90
  isSampled (span) {
90
91
  const priority = this._getPriorityFromAuto(span)
@@ -93,9 +94,10 @@ class PrioritySampler {
93
94
  }
94
95
 
95
96
  /**
97
+ * Assigns a sampling priority to a span if not already set.
96
98
  *
97
- * @param span {DatadogSpan}
98
- * @param auto {boolean}
99
+ * @param {DatadogSpan} span
100
+ * @param {boolean} [auto=true] - Whether to use automatic sampling if no manual tags are present.
99
101
  * @returns {void}
100
102
  */
101
103
  sample (span, auto = true) {
@@ -125,8 +127,9 @@ class PrioritySampler {
125
127
  }
126
128
 
127
129
  /**
130
+ * Updates agent-provided sampling rates keyed by `service:,env:`.
128
131
  *
129
- * @param rates {Record<string, number>}
132
+ * @param {Record<string, number>} rates
130
133
  * @returns {void}
131
134
  */
132
135
  update (rates) {
@@ -145,8 +148,9 @@ class PrioritySampler {
145
148
  }
146
149
 
147
150
  /**
151
+ * Validates that a sampling priority value is one of the allowed constants.
148
152
  *
149
- * @param samplingPriority {SamplingPriority}
153
+ * @param {SamplingPriority|undefined} samplingPriority
150
154
  * @returns {boolean}
151
155
  */
152
156
  validate (samplingPriority) {
@@ -162,10 +166,11 @@ class PrioritySampler {
162
166
  }
163
167
 
164
168
  /**
169
+ * Explicitly sets the priority and mechanism for the span's trace.
165
170
  *
166
- * @param span {DatadogSpan}
167
- * @param samplingPriority {SamplingPriority}
168
- * @param product {import('./standalone/product')}
171
+ * @param {DatadogSpan} span
172
+ * @param {SamplingPriority} samplingPriority
173
+ * @param {Product} [product]
169
174
  */
170
175
  setPriority (span, samplingPriority, product) {
171
176
  if (!span || !this.validate(samplingPriority)) return
@@ -189,17 +194,21 @@ class PrioritySampler {
189
194
  }
190
195
 
191
196
  /**
197
+ * Returns the span context, accepting either a span or a span context.
192
198
  *
193
- * @param span {DatadogSpan}
199
+ * @param {DatadogSpan|DatadogSpanContext} span
194
200
  * @returns {DatadogSpanContext}
195
201
  */
196
202
  _getContext (span) {
197
- return typeof span.context === 'function' ? span.context() : span
203
+ return typeof /** @type {DatadogSpan} */ (span).context === 'function'
204
+ ? /** @type {DatadogSpan} */ (span).context()
205
+ : /** @type {DatadogSpanContext} */ (span)
198
206
  }
199
207
 
200
208
  /**
209
+ * Computes priority using rules and agent rates when no manual tag is present.
201
210
  *
202
- * @param span {DatadogSpan}
211
+ * @param {DatadogSpan} span
203
212
  * @returns {SamplingPriority}
204
213
  */
205
214
  _getPriorityFromAuto (span) {
@@ -212,11 +221,12 @@ class PrioritySampler {
212
221
  }
213
222
 
214
223
  /**
215
- *
216
- * @param tags {Record<string, symbol | unknown>}
224
+ * Computes priority from manual sampling tags if present.
217
225
  * Included for compatibility with {@link import('./standalone/tracesource_priority_sampler')._getPriorityFromTags}
218
- * @param _context {DatadogSpanContext}
219
- * @returns {SamplingPriority}
226
+ *
227
+ * @param {Record<string, unknown>} tags
228
+ * @param {DatadogSpanContext} _context
229
+ * @returns {SamplingPriority|undefined}
220
230
  */
221
231
  _getPriorityFromTags (tags, _context) {
222
232
  if (Object.hasOwn(tags, MANUAL_KEEP) && tags[MANUAL_KEEP] !== false) {
@@ -224,19 +234,23 @@ class PrioritySampler {
224
234
  } else if (Object.hasOwn(tags, MANUAL_DROP) && tags[MANUAL_DROP] !== false) {
225
235
  return USER_REJECT
226
236
  }
227
- const priority = Number.parseInt(tags[SAMPLING_PRIORITY], 10)
228
-
229
- if (priority === 1 || priority === 2) {
230
- return USER_KEEP
231
- } else if (priority === 0 || priority === -1) {
232
- return USER_REJECT
237
+ const rawPriority = tags[SAMPLING_PRIORITY]
238
+ if (rawPriority !== undefined) {
239
+ const priority = Number.parseInt(String(rawPriority), 10)
240
+
241
+ if (priority === 1 || priority === 2) {
242
+ return USER_KEEP
243
+ } else if (priority === 0 || priority === -1) {
244
+ return USER_REJECT
245
+ }
233
246
  }
234
247
  }
235
248
 
236
249
  /**
250
+ * Applies a matching rule and rate limit to compute the sampling priority.
237
251
  *
238
- * @param context {DatadogSpanContext}
239
- * @param rule {SamplingRule}
252
+ * @param {DatadogSpanContext} context
253
+ * @param {import('./sampling_rule')} rule
240
254
  * @returns {SamplingPriority}
241
255
  */
242
256
  #getPriorityByRule (context, rule) {
@@ -251,12 +265,14 @@ class PrioritySampler {
251
265
  }
252
266
 
253
267
  /**
268
+ * Checks if the rate limiter allows sampling for the current window and
269
+ * records the effective rate on the trace.
254
270
  *
255
- * @param context {DatadogSpanContext}
271
+ * @param {DatadogSpanContext} context
256
272
  * @returns {boolean}
257
- * @private
258
273
  */
259
274
  _isSampledByRateLimit (context) {
275
+ // TODO: Change underscored properties to private ones.
260
276
  const allowed = this._limiter.isAllowed()
261
277
 
262
278
  context._trace[SAMPLING_LIMIT_DECISION] = this._limiter.effectiveRate()
@@ -265,12 +281,14 @@ class PrioritySampler {
265
281
  }
266
282
 
267
283
  /**
284
+ * Computes priority using agent-provided sampling rates.
268
285
  *
269
- * @param context {DatadogSpanContext}
286
+ * @param {DatadogSpanContext} context
270
287
  * @returns {SamplingPriority}
271
288
  */
272
289
  #getPriorityByAgent (context) {
273
290
  const key = `service:${context._tags[SERVICE_NAME]},env:${this._env}`
291
+ // TODO: Change underscored properties to private ones.
274
292
  const sampler = this._samplers[key] || this._samplers[DEFAULT_KEY]
275
293
 
276
294
  context._trace[SAMPLING_AGENT_DECISION] = sampler.rate()
@@ -281,8 +299,9 @@ class PrioritySampler {
281
299
  }
282
300
 
283
301
  /**
302
+ * Tags the trace with a decision maker when priority is keep, or removes it otherwise.
284
303
  *
285
- * @param span {DatadogSpan}
304
+ * @param {DatadogSpan} span
286
305
  * @returns {void}
287
306
  */
288
307
  #addDecisionMaker (span) {
@@ -301,11 +320,13 @@ class PrioritySampler {
301
320
  }
302
321
 
303
322
  /**
304
- * @param {Record<string, unknown>[] | Record<string, unknown>} rules - The sampling rules to normalize.
305
- * @param {number} sampleRate
323
+ * Normalizes rule inputs to SamplingRule instances, applying defaults.
324
+ *
325
+ * @param {Array<SamplingRuleLike>|SamplingRuleLike} rules - Rules to normalize.
326
+ * @param {number|undefined} sampleRate
306
327
  * @param {number} rateLimit
307
- * @param {string} provenance
308
- * @returns {SamplingRule[]}
328
+ * @param {string|undefined} provenance
329
+ * @returns {Array<import('./sampling_rule')>}
309
330
  */
310
331
  #normalizeRules (rules, sampleRate, rateLimit, provenance) {
311
332
  rules = Array.isArray(rules) ? rules.flat() : [rules]
@@ -314,7 +335,7 @@ class PrioritySampler {
314
335
 
315
336
  const result = []
316
337
  for (const rule of rules) {
317
- const sampleRate = Number.parseFloat(rule.sampleRate)
338
+ const sampleRate = Number.parseFloat(String(rule.sampleRate))
318
339
  // TODO(BridgeAR): Debug logging invalid rules fails our tests.
319
340
  // Should we definitely not know about these?
320
341
  if (!Number.isNaN(sampleRate)) {
@@ -325,11 +346,13 @@ class PrioritySampler {
325
346
  }
326
347
 
327
348
  /**
349
+ * Finds the first matching rule for the given span.
328
350
  *
329
- * @param span {DatadogSpan}
330
- * @returns {SamplingRule|undefined}
351
+ * @param {DatadogSpan} span
352
+ * @returns {import('./sampling_rule')|undefined}
331
353
  */
332
354
  #findRule (span) {
355
+ // TODO: Change underscored properties to private ones.
333
356
  for (const rule of this._rules) {
334
357
  // Rule is a special object with a .match() property.
335
358
  // It has nothing to do with a regular expression.
@@ -339,9 +362,10 @@ class PrioritySampler {
339
362
  }
340
363
 
341
364
  /**
365
+ * Convenience helper to keep a trace with an optional product mechanism.
342
366
  *
343
- * @param span {DatadogSpan}
344
- * @param product {import('./standalone/product')}
367
+ * @param {DatadogSpan} span
368
+ * @param {Product} [product]
345
369
  */
346
370
  static keepTrace (span, product) {
347
371
  span?._prioritySampler?.setPriority(span, USER_KEEP, product)