@sentry/profiling-node 8.0.0-alpha.2 → 8.0.0-alpha.3

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/lib/index.js CHANGED
@@ -7,33 +7,26 @@ const nodeAbi = require('node-abi');
7
7
  const process$1 = require('process');
8
8
  const worker_threads = require('worker_threads');
9
9
 
10
- /******************************************************************************
11
- Copyright (c) Microsoft Corporation.
12
-
13
- Permission to use, copy, modify, and/or distribute this software for any
14
- purpose with or without fee is hereby granted.
15
-
16
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
17
- REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
18
- AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
19
- INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
20
- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
21
- OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22
- PERFORMANCE OF THIS SOFTWARE.
23
- ***************************************************************************** */
24
-
25
- function __awaiter(thisArg, _arguments, P, generator) {
26
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
- return new (P || (P = Promise))(function (resolve, reject) {
28
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
29
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
30
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
31
- step((generator = generator.apply(thisArg, _arguments || [])).next());
32
- });
33
- }
34
-
35
10
  // eslint-disable-next-line @typescript-eslint/unbound-method
36
11
  const objectToString = Object.prototype.toString;
12
+
13
+ /**
14
+ * Checks whether given value's type is one of a few Error or Error-like
15
+ * {@link isError}.
16
+ *
17
+ * @param wat A value to be checked.
18
+ * @returns A boolean representing the result.
19
+ */
20
+ function isError(wat) {
21
+ switch (objectToString.call(wat)) {
22
+ case '[object Error]':
23
+ case '[object Exception]':
24
+ case '[object DOMException]':
25
+ return true;
26
+ default:
27
+ return isInstanceOf(wat, Error);
28
+ }
29
+ }
37
30
  /**
38
31
  * Checks whether given value is an instance of the given built-in class.
39
32
  *
@@ -45,6 +38,17 @@ function isBuiltin(wat, className) {
45
38
  return objectToString.call(wat) === `[object ${className}]`;
46
39
  }
47
40
 
41
+ /**
42
+ * Checks whether given value's type is a string
43
+ * {@link isString}.
44
+ *
45
+ * @param wat A value to be checked.
46
+ * @returns A boolean representing the result.
47
+ */
48
+ function isString(wat) {
49
+ return isBuiltin(wat, 'String');
50
+ }
51
+
48
52
  /**
49
53
  * Checks whether given value's type is an object literal, or a class instance.
50
54
  * {@link isPlainObject}.
@@ -56,6 +60,28 @@ function isPlainObject(wat) {
56
60
  return isBuiltin(wat, 'Object');
57
61
  }
58
62
 
63
+ /**
64
+ * Checks whether given value's type is an Event instance
65
+ * {@link isEvent}.
66
+ *
67
+ * @param wat A value to be checked.
68
+ * @returns A boolean representing the result.
69
+ */
70
+ function isEvent(wat) {
71
+ return typeof Event !== 'undefined' && isInstanceOf(wat, Event);
72
+ }
73
+
74
+ /**
75
+ * Checks whether given value's type is an Element instance
76
+ * {@link isElement}.
77
+ *
78
+ * @param wat A value to be checked.
79
+ * @returns A boolean representing the result.
80
+ */
81
+ function isElement(wat) {
82
+ return typeof Element !== 'undefined' && isInstanceOf(wat, Element);
83
+ }
84
+
59
85
  /**
60
86
  * Checks whether given value has a then function.
61
87
  * @param wat A value to be checked.
@@ -65,6 +91,55 @@ function isThenable(wat) {
65
91
  return Boolean(wat && wat.then && typeof wat.then === 'function');
66
92
  }
67
93
 
94
+ /**
95
+ * Checks whether given value's type is a SyntheticEvent
96
+ * {@link isSyntheticEvent}.
97
+ *
98
+ * @param wat A value to be checked.
99
+ * @returns A boolean representing the result.
100
+ */
101
+ function isSyntheticEvent(wat) {
102
+ return isPlainObject(wat) && 'nativeEvent' in wat && 'preventDefault' in wat && 'stopPropagation' in wat;
103
+ }
104
+
105
+ /**
106
+ * Checks whether given value is NaN
107
+ * {@link isNaN}.
108
+ *
109
+ * @param wat A value to be checked.
110
+ * @returns A boolean representing the result.
111
+ */
112
+ function isNaN$1(wat) {
113
+ return typeof wat === 'number' && wat !== wat;
114
+ }
115
+
116
+ /**
117
+ * Checks whether given value's type is an instance of provided constructor.
118
+ * {@link isInstanceOf}.
119
+ *
120
+ * @param wat A value to be checked.
121
+ * @param base A constructor to be used in a check.
122
+ * @returns A boolean representing the result.
123
+ */
124
+ function isInstanceOf(wat, base) {
125
+ try {
126
+ return wat instanceof base;
127
+ } catch (_e) {
128
+ return false;
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Checks whether given value's type is a Vue ViewModel.
134
+ *
135
+ * @param wat A value to be checked.
136
+ * @returns A boolean representing the result.
137
+ */
138
+ function isVueViewModel(wat) {
139
+ // Not using Object.prototype.toString because in Vue 3 it would read the instance's Symbol(Symbol.toStringTag) property.
140
+ return !!(typeof wat === 'object' && wat !== null && ((wat ).__isVue || (wat )._isVue));
141
+ }
142
+
68
143
  /** Internal global with common properties and Sentry extensions */
69
144
 
70
145
  // The code below for 'isGlobalObj' and 'GLOBAL_OBJ' was copied from core-js before modification
@@ -108,6 +183,153 @@ const GLOBAL_OBJ =
108
183
  })() ||
109
184
  {};
110
185
 
186
+ /**
187
+ * @deprecated Use GLOBAL_OBJ instead or WINDOW from @sentry/browser. This will be removed in v8
188
+ */
189
+ function getGlobalObject() {
190
+ return GLOBAL_OBJ ;
191
+ }
192
+
193
+ /**
194
+ * Returns a global singleton contained in the global `__SENTRY__` object.
195
+ *
196
+ * If the singleton doesn't already exist in `__SENTRY__`, it will be created using the given factory
197
+ * function and added to the `__SENTRY__` object.
198
+ *
199
+ * @param name name of the global singleton on __SENTRY__
200
+ * @param creator creator Factory function to create the singleton if it doesn't already exist on `__SENTRY__`
201
+ * @param obj (Optional) The global object on which to look for `__SENTRY__`, if not `GLOBAL_OBJ`'s return value
202
+ * @returns the singleton
203
+ */
204
+ function getGlobalSingleton(name, creator, obj) {
205
+ const gbl = (obj || GLOBAL_OBJ) ;
206
+ const __SENTRY__ = (gbl.__SENTRY__ = gbl.__SENTRY__ || {});
207
+ const singleton = __SENTRY__[name] || (__SENTRY__[name] = creator());
208
+ return singleton;
209
+ }
210
+
211
+ // eslint-disable-next-line deprecation/deprecation
212
+ const WINDOW$5 = getGlobalObject();
213
+
214
+ const DEFAULT_MAX_STRING_LENGTH = 80;
215
+
216
+ /**
217
+ * Given a child DOM element, returns a query-selector statement describing that
218
+ * and its ancestors
219
+ * e.g. [HTMLElement] => body > div > input#foo.btn[name=baz]
220
+ * @returns generated DOM path
221
+ */
222
+ function htmlTreeAsString(
223
+ elem,
224
+ options = {},
225
+ ) {
226
+ if (!elem) {
227
+ return '<unknown>';
228
+ }
229
+
230
+ // try/catch both:
231
+ // - accessing event.target (see getsentry/raven-js#838, #768)
232
+ // - `htmlTreeAsString` because it's complex, and just accessing the DOM incorrectly
233
+ // - can throw an exception in some circumstances.
234
+ try {
235
+ let currentElem = elem ;
236
+ const MAX_TRAVERSE_HEIGHT = 5;
237
+ const out = [];
238
+ let height = 0;
239
+ let len = 0;
240
+ const separator = ' > ';
241
+ const sepLength = separator.length;
242
+ let nextStr;
243
+ const keyAttrs = Array.isArray(options) ? options : options.keyAttrs;
244
+ const maxStringLength = (!Array.isArray(options) && options.maxStringLength) || DEFAULT_MAX_STRING_LENGTH;
245
+
246
+ while (currentElem && height++ < MAX_TRAVERSE_HEIGHT) {
247
+ nextStr = _htmlElementAsString(currentElem, keyAttrs);
248
+ // bail out if
249
+ // - nextStr is the 'html' element
250
+ // - the length of the string that would be created exceeds maxStringLength
251
+ // (ignore this limit if we are on the first iteration)
252
+ if (nextStr === 'html' || (height > 1 && len + out.length * sepLength + nextStr.length >= maxStringLength)) {
253
+ break;
254
+ }
255
+
256
+ out.push(nextStr);
257
+
258
+ len += nextStr.length;
259
+ currentElem = currentElem.parentNode;
260
+ }
261
+
262
+ return out.reverse().join(separator);
263
+ } catch (_oO) {
264
+ return '<unknown>';
265
+ }
266
+ }
267
+
268
+ /**
269
+ * Returns a simple, query-selector representation of a DOM element
270
+ * e.g. [HTMLElement] => input#foo.btn[name=baz]
271
+ * @returns generated DOM path
272
+ */
273
+ function _htmlElementAsString(el, keyAttrs) {
274
+ const elem = el
275
+
276
+ ;
277
+
278
+ const out = [];
279
+ let className;
280
+ let classes;
281
+ let key;
282
+ let attr;
283
+ let i;
284
+
285
+ if (!elem || !elem.tagName) {
286
+ return '';
287
+ }
288
+
289
+ // @ts-expect-error WINDOW has HTMLElement
290
+ if (WINDOW$5.HTMLElement) {
291
+ // If using the component name annotation plugin, this value may be available on the DOM node
292
+ if (elem instanceof HTMLElement && elem.dataset && elem.dataset['sentryComponent']) {
293
+ return elem.dataset['sentryComponent'];
294
+ }
295
+ }
296
+
297
+ out.push(elem.tagName.toLowerCase());
298
+
299
+ // Pairs of attribute keys defined in `serializeAttribute` and their values on element.
300
+ const keyAttrPairs =
301
+ keyAttrs && keyAttrs.length
302
+ ? keyAttrs.filter(keyAttr => elem.getAttribute(keyAttr)).map(keyAttr => [keyAttr, elem.getAttribute(keyAttr)])
303
+ : null;
304
+
305
+ if (keyAttrPairs && keyAttrPairs.length) {
306
+ keyAttrPairs.forEach(keyAttrPair => {
307
+ out.push(`[${keyAttrPair[0]}="${keyAttrPair[1]}"]`);
308
+ });
309
+ } else {
310
+ if (elem.id) {
311
+ out.push(`#${elem.id}`);
312
+ }
313
+
314
+ className = elem.className;
315
+ if (className && isString(className)) {
316
+ classes = className.split(/\s+/);
317
+ for (i = 0; i < classes.length; i++) {
318
+ out.push(`.${classes[i]}`);
319
+ }
320
+ }
321
+ }
322
+ const allowedAttrs = ['aria-label', 'type', 'name', 'title', 'alt'];
323
+ for (i = 0; i < allowedAttrs.length; i++) {
324
+ key = allowedAttrs[i];
325
+ attr = elem.getAttribute(key);
326
+ if (attr) {
327
+ out.push(`[${key}="${attr}"]`);
328
+ }
329
+ }
330
+ return out.join('');
331
+ }
332
+
111
333
  /**
112
334
  * This serves as a build time flag that will be true by default, but false in non-debug builds or if users replace `__SENTRY_DEBUG__` in their generated code.
113
335
  *
@@ -202,21 +424,97 @@ function makeLogger() {
202
424
 
203
425
  const logger = makeLogger();
204
426
 
427
+ const defaultFunctionName = '<anonymous>';
428
+
205
429
  /**
206
- * Renders the string representation of this Dsn.
430
+ * Safely extract function name from itself
431
+ */
432
+ function getFunctionName(fn) {
433
+ try {
434
+ if (!fn || typeof fn !== 'function') {
435
+ return defaultFunctionName;
436
+ }
437
+ return fn.name || defaultFunctionName;
438
+ } catch (e) {
439
+ // Just accessing custom props in some Selenium environments
440
+ // can cause a "Permission denied" exception (see raven-js#495).
441
+ return defaultFunctionName;
442
+ }
443
+ }
444
+
445
+ /**
446
+ * This extracts the original function if available. See
447
+ * `markFunctionWrapped` for more information.
207
448
  *
208
- * By default, this will render the public representation without the password
209
- * component. To get the deprecated private representation, set `withPassword`
210
- * to true.
449
+ * @param func the function to unwrap
450
+ * @returns the unwrapped version of the function if available.
451
+ */
452
+ function getOriginalFunction(func) {
453
+ return func.__sentry_original__;
454
+ }
455
+
456
+ /**
457
+ * Transforms any `Error` or `Event` into a plain object with all of their enumerable properties, and some of their
458
+ * non-enumerable properties attached.
211
459
  *
212
- * @param withPassword When set to true, the password will be included.
460
+ * @param value Initial source that we have to transform in order for it to be usable by the serializer
461
+ * @returns An Event or Error turned into an object - or the value argurment itself, when value is neither an Event nor
462
+ * an Error.
213
463
  */
214
- function dsnToString(dsn, withPassword = false) {
215
- const { host, path, pass, port, projectId, protocol, publicKey } = dsn;
216
- return (
217
- `${protocol}://${publicKey}${withPassword && pass ? `:${pass}` : ''}` +
218
- `@${host}${port ? `:${port}` : ''}/${path ? `${path}/` : path}${projectId}`
219
- );
464
+ function convertToPlainObject(
465
+ value,
466
+ )
467
+
468
+ {
469
+ if (isError(value)) {
470
+ return {
471
+ message: value.message,
472
+ name: value.name,
473
+ stack: value.stack,
474
+ ...getOwnProperties(value),
475
+ };
476
+ } else if (isEvent(value)) {
477
+ const newObj
478
+
479
+ = {
480
+ type: value.type,
481
+ target: serializeEventTarget(value.target),
482
+ currentTarget: serializeEventTarget(value.currentTarget),
483
+ ...getOwnProperties(value),
484
+ };
485
+
486
+ if (typeof CustomEvent !== 'undefined' && isInstanceOf(value, CustomEvent)) {
487
+ newObj.detail = value.detail;
488
+ }
489
+
490
+ return newObj;
491
+ } else {
492
+ return value;
493
+ }
494
+ }
495
+
496
+ /** Creates a string representation of the target of an `Event` object */
497
+ function serializeEventTarget(target) {
498
+ try {
499
+ return isElement(target) ? htmlTreeAsString(target) : Object.prototype.toString.call(target);
500
+ } catch (_oO) {
501
+ return '<unknown>';
502
+ }
503
+ }
504
+
505
+ /** Filters out all but an object's own properties */
506
+ function getOwnProperties(obj) {
507
+ if (typeof obj === 'object' && obj !== null) {
508
+ const extractedProps = {};
509
+ for (const property in obj) {
510
+ if (Object.prototype.hasOwnProperty.call(obj, property)) {
511
+ extractedProps[property] = (obj )[property];
512
+ }
513
+ }
514
+ return extractedProps;
515
+ } else {
516
+ return {};
517
+ }
220
518
  }
221
519
 
222
520
  /**
@@ -304,28 +602,693 @@ function uuid4() {
304
602
  if (crypto && crypto.randomUUID) {
305
603
  return crypto.randomUUID().replace(/-/g, '');
306
604
  }
307
- if (crypto && crypto.getRandomValues) {
308
- getRandomByte = () => {
309
- // crypto.getRandomValues might return undefined instead of the typed array
310
- // in old Chromium versions (e.g. 23.0.1235.0 (151422))
311
- // However, `typedArray` is still filled in-place.
312
- // @see https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues#typedarray
313
- const typedArray = new Uint8Array(1);
314
- crypto.getRandomValues(typedArray);
315
- return typedArray[0];
605
+ if (crypto && crypto.getRandomValues) {
606
+ getRandomByte = () => {
607
+ // crypto.getRandomValues might return undefined instead of the typed array
608
+ // in old Chromium versions (e.g. 23.0.1235.0 (151422))
609
+ // However, `typedArray` is still filled in-place.
610
+ // @see https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues#typedarray
611
+ const typedArray = new Uint8Array(1);
612
+ crypto.getRandomValues(typedArray);
613
+ return typedArray[0];
614
+ };
615
+ }
616
+ } catch (_) {
617
+ // some runtimes can crash invoking crypto
618
+ // https://github.com/getsentry/sentry-javascript/issues/8935
619
+ }
620
+
621
+ // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/2117523#2117523
622
+ // Concatenating the following numbers as strings results in '10000000100040008000100000000000'
623
+ return (([1e7] ) + 1e3 + 4e3 + 8e3 + 1e11).replace(/[018]/g, c =>
624
+ // eslint-disable-next-line no-bitwise
625
+ ((c ) ^ ((getRandomByte() & 15) >> ((c ) / 4))).toString(16),
626
+ );
627
+ }
628
+
629
+ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
630
+ /* eslint-disable @typescript-eslint/no-explicit-any */
631
+
632
+ /**
633
+ * Helper to decycle json objects
634
+ */
635
+ function memoBuilder() {
636
+ const hasWeakSet = typeof WeakSet === 'function';
637
+ const inner = hasWeakSet ? new WeakSet() : [];
638
+ function memoize(obj) {
639
+ if (hasWeakSet) {
640
+ if (inner.has(obj)) {
641
+ return true;
642
+ }
643
+ inner.add(obj);
644
+ return false;
645
+ }
646
+ // eslint-disable-next-line @typescript-eslint/prefer-for-of
647
+ for (let i = 0; i < inner.length; i++) {
648
+ const value = inner[i];
649
+ if (value === obj) {
650
+ return true;
651
+ }
652
+ }
653
+ inner.push(obj);
654
+ return false;
655
+ }
656
+
657
+ function unmemoize(obj) {
658
+ if (hasWeakSet) {
659
+ inner.delete(obj);
660
+ } else {
661
+ for (let i = 0; i < inner.length; i++) {
662
+ if (inner[i] === obj) {
663
+ inner.splice(i, 1);
664
+ break;
665
+ }
666
+ }
667
+ }
668
+ }
669
+ return [memoize, unmemoize];
670
+ }
671
+
672
+ /**
673
+ * Recursively normalizes the given object.
674
+ *
675
+ * - Creates a copy to prevent original input mutation
676
+ * - Skips non-enumerable properties
677
+ * - When stringifying, calls `toJSON` if implemented
678
+ * - Removes circular references
679
+ * - Translates non-serializable values (`undefined`/`NaN`/functions) to serializable format
680
+ * - Translates known global objects/classes to a string representations
681
+ * - Takes care of `Error` object serialization
682
+ * - Optionally limits depth of final output
683
+ * - Optionally limits number of properties/elements included in any single object/array
684
+ *
685
+ * @param input The object to be normalized.
686
+ * @param depth The max depth to which to normalize the object. (Anything deeper stringified whole.)
687
+ * @param maxProperties The max number of elements or properties to be included in any single array or
688
+ * object in the normallized output.
689
+ * @returns A normalized version of the object, or `"**non-serializable**"` if any errors are thrown during normalization.
690
+ */
691
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
692
+ function normalize(input, depth = 100, maxProperties = +Infinity) {
693
+ try {
694
+ // since we're at the outermost level, we don't provide a key
695
+ return visit('', input, depth, maxProperties);
696
+ } catch (err) {
697
+ return { ERROR: `**non-serializable** (${err})` };
698
+ }
699
+ }
700
+
701
+ /**
702
+ * Visits a node to perform normalization on it
703
+ *
704
+ * @param key The key corresponding to the given node
705
+ * @param value The node to be visited
706
+ * @param depth Optional number indicating the maximum recursion depth
707
+ * @param maxProperties Optional maximum number of properties/elements included in any single object/array
708
+ * @param memo Optional Memo class handling decycling
709
+ */
710
+ function visit(
711
+ key,
712
+ value,
713
+ depth = +Infinity,
714
+ maxProperties = +Infinity,
715
+ memo = memoBuilder(),
716
+ ) {
717
+ const [memoize, unmemoize] = memo;
718
+
719
+ // Get the simple cases out of the way first
720
+ if (
721
+ value == null || // this matches null and undefined -> eqeq not eqeqeq
722
+ (['number', 'boolean', 'string'].includes(typeof value) && !isNaN$1(value))
723
+ ) {
724
+ return value ;
725
+ }
726
+
727
+ const stringified = stringifyValue(key, value);
728
+
729
+ // Anything we could potentially dig into more (objects or arrays) will have come back as `"[object XXXX]"`.
730
+ // Everything else will have already been serialized, so if we don't see that pattern, we're done.
731
+ if (!stringified.startsWith('[object ')) {
732
+ return stringified;
733
+ }
734
+
735
+ // From here on, we can assert that `value` is either an object or an array.
736
+
737
+ // Do not normalize objects that we know have already been normalized. As a general rule, the
738
+ // "__sentry_skip_normalization__" property should only be used sparingly and only should only be set on objects that
739
+ // have already been normalized.
740
+ if ((value )['__sentry_skip_normalization__']) {
741
+ return value ;
742
+ }
743
+
744
+ // We can set `__sentry_override_normalization_depth__` on an object to ensure that from there
745
+ // We keep a certain amount of depth.
746
+ // This should be used sparingly, e.g. we use it for the redux integration to ensure we get a certain amount of state.
747
+ const remainingDepth =
748
+ typeof (value )['__sentry_override_normalization_depth__'] === 'number'
749
+ ? ((value )['__sentry_override_normalization_depth__'] )
750
+ : depth;
751
+
752
+ // We're also done if we've reached the max depth
753
+ if (remainingDepth === 0) {
754
+ // At this point we know `serialized` is a string of the form `"[object XXXX]"`. Clean it up so it's just `"[XXXX]"`.
755
+ return stringified.replace('object ', '');
756
+ }
757
+
758
+ // If we've already visited this branch, bail out, as it's circular reference. If not, note that we're seeing it now.
759
+ if (memoize(value)) {
760
+ return '[Circular ~]';
761
+ }
762
+
763
+ // If the value has a `toJSON` method, we call it to extract more information
764
+ const valueWithToJSON = value ;
765
+ if (valueWithToJSON && typeof valueWithToJSON.toJSON === 'function') {
766
+ try {
767
+ const jsonValue = valueWithToJSON.toJSON();
768
+ // We need to normalize the return value of `.toJSON()` in case it has circular references
769
+ return visit('', jsonValue, remainingDepth - 1, maxProperties, memo);
770
+ } catch (err) {
771
+ // pass (The built-in `toJSON` failed, but we can still try to do it ourselves)
772
+ }
773
+ }
774
+
775
+ // At this point we know we either have an object or an array, we haven't seen it before, and we're going to recurse
776
+ // because we haven't yet reached the max depth. Create an accumulator to hold the results of visiting each
777
+ // property/entry, and keep track of the number of items we add to it.
778
+ const normalized = (Array.isArray(value) ? [] : {}) ;
779
+ let numAdded = 0;
780
+
781
+ // Before we begin, convert`Error` and`Event` instances into plain objects, since some of each of their relevant
782
+ // properties are non-enumerable and otherwise would get missed.
783
+ const visitable = convertToPlainObject(value );
784
+
785
+ for (const visitKey in visitable) {
786
+ // Avoid iterating over fields in the prototype if they've somehow been exposed to enumeration.
787
+ if (!Object.prototype.hasOwnProperty.call(visitable, visitKey)) {
788
+ continue;
789
+ }
790
+
791
+ if (numAdded >= maxProperties) {
792
+ normalized[visitKey] = '[MaxProperties ~]';
793
+ break;
794
+ }
795
+
796
+ // Recursively visit all the child nodes
797
+ const visitValue = visitable[visitKey];
798
+ normalized[visitKey] = visit(visitKey, visitValue, remainingDepth - 1, maxProperties, memo);
799
+
800
+ numAdded++;
801
+ }
802
+
803
+ // Once we've visited all the branches, remove the parent from memo storage
804
+ unmemoize(value);
805
+
806
+ // Return accumulated values
807
+ return normalized;
808
+ }
809
+
810
+ /* eslint-disable complexity */
811
+ /**
812
+ * Stringify the given value. Handles various known special values and types.
813
+ *
814
+ * Not meant to be used on simple primitives which already have a string representation, as it will, for example, turn
815
+ * the number 1231 into "[Object Number]", nor on `null`, as it will throw.
816
+ *
817
+ * @param value The value to stringify
818
+ * @returns A stringified representation of the given value
819
+ */
820
+ function stringifyValue(
821
+ key,
822
+ // this type is a tiny bit of a cheat, since this function does handle NaN (which is technically a number), but for
823
+ // our internal use, it'll do
824
+ value,
825
+ ) {
826
+ try {
827
+ if (key === 'domain' && value && typeof value === 'object' && (value )._events) {
828
+ return '[Domain]';
829
+ }
830
+
831
+ if (key === 'domainEmitter') {
832
+ return '[DomainEmitter]';
833
+ }
834
+
835
+ // It's safe to use `global`, `window`, and `document` here in this manner, as we are asserting using `typeof` first
836
+ // which won't throw if they are not present.
837
+
838
+ if (typeof global !== 'undefined' && value === global) {
839
+ return '[Global]';
840
+ }
841
+
842
+ // eslint-disable-next-line no-restricted-globals
843
+ if (typeof window !== 'undefined' && value === window) {
844
+ return '[Window]';
845
+ }
846
+
847
+ // eslint-disable-next-line no-restricted-globals
848
+ if (typeof document !== 'undefined' && value === document) {
849
+ return '[Document]';
850
+ }
851
+
852
+ if (isVueViewModel(value)) {
853
+ return '[VueViewModel]';
854
+ }
855
+
856
+ // React's SyntheticEvent thingy
857
+ if (isSyntheticEvent(value)) {
858
+ return '[SyntheticEvent]';
859
+ }
860
+
861
+ if (typeof value === 'number' && value !== value) {
862
+ return '[NaN]';
863
+ }
864
+
865
+ if (typeof value === 'function') {
866
+ return `[Function: ${getFunctionName(value)}]`;
867
+ }
868
+
869
+ if (typeof value === 'symbol') {
870
+ return `[${String(value)}]`;
871
+ }
872
+
873
+ // stringified BigInts are indistinguishable from regular numbers, so we need to label them to avoid confusion
874
+ if (typeof value === 'bigint') {
875
+ return `[BigInt: ${String(value)}]`;
876
+ }
877
+
878
+ // Now that we've knocked out all the special cases and the primitives, all we have left are objects. Simply casting
879
+ // them to strings means that instances of classes which haven't defined their `toStringTag` will just come out as
880
+ // `"[object Object]"`. If we instead look at the constructor's name (which is the same as the name of the class),
881
+ // we can make sure that only plain objects come out that way.
882
+ const objName = getConstructorName(value);
883
+
884
+ // Handle HTML Elements
885
+ if (/^HTML(\w*)Element$/.test(objName)) {
886
+ return `[HTMLElement: ${objName}]`;
887
+ }
888
+
889
+ return `[object ${objName}]`;
890
+ } catch (err) {
891
+ return `**non-serializable** (${err})`;
892
+ }
893
+ }
894
+ /* eslint-enable complexity */
895
+
896
+ function getConstructorName(value) {
897
+ const prototype = Object.getPrototypeOf(value);
898
+
899
+ return prototype ? prototype.constructor.name : 'null prototype';
900
+ }
901
+
902
+ /* eslint-disable @typescript-eslint/explicit-function-return-type */
903
+ /* eslint-disable @typescript-eslint/no-explicit-any */
904
+
905
+ /** SyncPromise internal states */
906
+ var States; (function (States) {
907
+ /** Pending */
908
+ const PENDING = 0; States[States["PENDING"] = PENDING] = "PENDING";
909
+ /** Resolved / OK */
910
+ const RESOLVED = 1; States[States["RESOLVED"] = RESOLVED] = "RESOLVED";
911
+ /** Rejected / Error */
912
+ const REJECTED = 2; States[States["REJECTED"] = REJECTED] = "REJECTED";
913
+ })(States || (States = {}));
914
+
915
+ /**
916
+ * This code was originally copied from the 'cookie` module at v0.5.0 and was simplified for our use case.
917
+ * https://github.com/jshttp/cookie/blob/a0c84147aab6266bdb3996cf4062e93907c0b0fc/index.js
918
+ * It had the following license:
919
+ *
920
+ * (The MIT License)
921
+ *
922
+ * Copyright (c) 2012-2014 Roman Shtylman <shtylman@gmail.com>
923
+ * Copyright (c) 2015 Douglas Christopher Wilson <doug@somethingdoug.com>
924
+ *
925
+ * Permission is hereby granted, free of charge, to any person obtaining
926
+ * a copy of this software and associated documentation files (the
927
+ * 'Software'), to deal in the Software without restriction, including
928
+ * without limitation the rights to use, copy, modify, merge, publish,
929
+ * distribute, sublicense, and/or sell copies of the Software, and to
930
+ * permit persons to whom the Software is furnished to do so, subject to
931
+ * the following conditions:
932
+ *
933
+ * The above copyright notice and this permission notice shall be
934
+ * included in all copies or substantial portions of the Software.
935
+ *
936
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
937
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
938
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
939
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
940
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
941
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
942
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
943
+ */
944
+
945
+ /**
946
+ * Parses a cookie string
947
+ */
948
+ function parseCookie(str) {
949
+ const obj = {};
950
+ let index = 0;
951
+
952
+ while (index < str.length) {
953
+ const eqIdx = str.indexOf('=', index);
954
+
955
+ // no more cookie pairs
956
+ if (eqIdx === -1) {
957
+ break;
958
+ }
959
+
960
+ let endIdx = str.indexOf(';', index);
961
+
962
+ if (endIdx === -1) {
963
+ endIdx = str.length;
964
+ } else if (endIdx < eqIdx) {
965
+ // backtrack on prior semicolon
966
+ index = str.lastIndexOf(';', eqIdx - 1) + 1;
967
+ continue;
968
+ }
969
+
970
+ const key = str.slice(index, eqIdx).trim();
971
+
972
+ // only assign once
973
+ if (undefined === obj[key]) {
974
+ let val = str.slice(eqIdx + 1, endIdx).trim();
975
+
976
+ // quoted values
977
+ if (val.charCodeAt(0) === 0x22) {
978
+ val = val.slice(1, -1);
979
+ }
980
+
981
+ try {
982
+ obj[key] = val.indexOf('%') !== -1 ? decodeURIComponent(val) : val;
983
+ } catch (e) {
984
+ obj[key] = val;
985
+ }
986
+ }
987
+
988
+ index = endIdx + 1;
989
+ }
990
+
991
+ return obj;
992
+ }
993
+
994
+ /**
995
+ * Strip the query string and fragment off of a given URL or path (if present)
996
+ *
997
+ * @param urlPath Full URL or path, including possible query string and/or fragment
998
+ * @returns URL or path without query string or fragment
999
+ */
1000
+ function stripUrlQueryAndFragment(urlPath) {
1001
+ // eslint-disable-next-line no-useless-escape
1002
+ return urlPath.split(/[\?#]/, 1)[0];
1003
+ }
1004
+
1005
+ const DEFAULT_INCLUDES = {
1006
+ ip: false,
1007
+ request: true,
1008
+ transaction: true,
1009
+ user: true,
1010
+ };
1011
+ const DEFAULT_REQUEST_INCLUDES = ['cookies', 'data', 'headers', 'method', 'query_string', 'url'];
1012
+ const DEFAULT_USER_INCLUDES = ['id', 'username', 'email'];
1013
+
1014
+ /**
1015
+ * Extracts a complete and parameterized path from the request object and uses it to construct transaction name.
1016
+ * If the parameterized transaction name cannot be extracted, we fall back to the raw URL.
1017
+ *
1018
+ * Additionally, this function determines and returns the transaction name source
1019
+ *
1020
+ * eg. GET /mountpoint/user/:id
1021
+ *
1022
+ * @param req A request object
1023
+ * @param options What to include in the transaction name (method, path, or a custom route name to be
1024
+ * used instead of the request's route)
1025
+ *
1026
+ * @returns A tuple of the fully constructed transaction name [0] and its source [1] (can be either 'route' or 'url')
1027
+ */
1028
+ function extractPathForTransaction(
1029
+ req,
1030
+ options = {},
1031
+ ) {
1032
+ const method = req.method && req.method.toUpperCase();
1033
+
1034
+ let path = '';
1035
+ let source = 'url';
1036
+
1037
+ // Check to see if there's a parameterized route we can use (as there is in Express)
1038
+ if (options.customRoute || req.route) {
1039
+ path = options.customRoute || `${req.baseUrl || ''}${req.route && req.route.path}`;
1040
+ source = 'route';
1041
+ }
1042
+
1043
+ // Otherwise, just take the original URL
1044
+ else if (req.originalUrl || req.url) {
1045
+ path = stripUrlQueryAndFragment(req.originalUrl || req.url || '');
1046
+ }
1047
+
1048
+ let name = '';
1049
+ if (options.method && method) {
1050
+ name += method;
1051
+ }
1052
+ if (options.method && options.path) {
1053
+ name += ' ';
1054
+ }
1055
+ if (options.path && path) {
1056
+ name += path;
1057
+ }
1058
+
1059
+ return [name, source];
1060
+ }
1061
+
1062
+ /** JSDoc */
1063
+ function extractTransaction(req, type) {
1064
+ switch (type) {
1065
+ case 'path': {
1066
+ return extractPathForTransaction(req, { path: true })[0];
1067
+ }
1068
+ case 'handler': {
1069
+ return (req.route && req.route.stack && req.route.stack[0] && req.route.stack[0].name) || '<anonymous>';
1070
+ }
1071
+ case 'methodPath':
1072
+ default: {
1073
+ // if exist _reconstructedRoute return that path instead of route.path
1074
+ const customRoute = req._reconstructedRoute ? req._reconstructedRoute : undefined;
1075
+ return extractPathForTransaction(req, { path: true, method: true, customRoute })[0];
1076
+ }
1077
+ }
1078
+ }
1079
+
1080
+ /** JSDoc */
1081
+ function extractUserData(
1082
+ user
1083
+
1084
+ ,
1085
+ keys,
1086
+ ) {
1087
+ const extractedUser = {};
1088
+ const attributes = Array.isArray(keys) ? keys : DEFAULT_USER_INCLUDES;
1089
+
1090
+ attributes.forEach(key => {
1091
+ if (user && key in user) {
1092
+ extractedUser[key] = user[key];
1093
+ }
1094
+ });
1095
+
1096
+ return extractedUser;
1097
+ }
1098
+
1099
+ /**
1100
+ * Normalize data from the request object, accounting for framework differences.
1101
+ *
1102
+ * @param req The request object from which to extract data
1103
+ * @param options.include An optional array of keys to include in the normalized data. Defaults to
1104
+ * DEFAULT_REQUEST_INCLUDES if not provided.
1105
+ * @param options.deps Injected, platform-specific dependencies
1106
+ * @returns An object containing normalized request data
1107
+ */
1108
+ function extractRequestData(
1109
+ req,
1110
+ options
1111
+
1112
+ ,
1113
+ ) {
1114
+ const { include = DEFAULT_REQUEST_INCLUDES } = options || {};
1115
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1116
+ const requestData = {};
1117
+
1118
+ // headers:
1119
+ // node, express, koa, nextjs: req.headers
1120
+ const headers = (req.headers || {})
1121
+
1122
+ ;
1123
+ // method:
1124
+ // node, express, koa, nextjs: req.method
1125
+ const method = req.method;
1126
+ // host:
1127
+ // express: req.hostname in > 4 and req.host in < 4
1128
+ // koa: req.host
1129
+ // node, nextjs: req.headers.host
1130
+ // Express 4 mistakenly strips off port number from req.host / req.hostname so we can't rely on them
1131
+ // See: https://github.com/expressjs/express/issues/3047#issuecomment-236653223
1132
+ // Also: https://github.com/getsentry/sentry-javascript/issues/1917
1133
+ const host = headers.host || req.hostname || req.host || '<no host>';
1134
+ // protocol:
1135
+ // node, nextjs: <n/a>
1136
+ // express, koa: req.protocol
1137
+ const protocol = req.protocol === 'https' || (req.socket && req.socket.encrypted) ? 'https' : 'http';
1138
+ // url (including path and query string):
1139
+ // node, express: req.originalUrl
1140
+ // koa, nextjs: req.url
1141
+ const originalUrl = req.originalUrl || req.url || '';
1142
+ // absolute url
1143
+ const absoluteUrl = originalUrl.startsWith(protocol) ? originalUrl : `${protocol}://${host}${originalUrl}`;
1144
+ include.forEach(key => {
1145
+ switch (key) {
1146
+ case 'headers': {
1147
+ requestData.headers = headers;
1148
+
1149
+ // Remove the Cookie header in case cookie data should not be included in the event
1150
+ if (!include.includes('cookies')) {
1151
+ delete (requestData.headers ).cookie;
1152
+ }
1153
+
1154
+ break;
1155
+ }
1156
+ case 'method': {
1157
+ requestData.method = method;
1158
+ break;
1159
+ }
1160
+ case 'url': {
1161
+ requestData.url = absoluteUrl;
1162
+ break;
1163
+ }
1164
+ case 'cookies': {
1165
+ // cookies:
1166
+ // node, express, koa: req.headers.cookie
1167
+ // vercel, sails.js, express (w/ cookie middleware), nextjs: req.cookies
1168
+ requestData.cookies =
1169
+ // TODO (v8 / #5257): We're only sending the empty object for backwards compatibility, so the last bit can
1170
+ // come off in v8
1171
+ req.cookies || (headers.cookie && parseCookie(headers.cookie)) || {};
1172
+ break;
1173
+ }
1174
+ case 'query_string': {
1175
+ // query string:
1176
+ // node: req.url (raw)
1177
+ // express, koa, nextjs: req.query
1178
+ requestData.query_string = extractQueryParams(req);
1179
+ break;
1180
+ }
1181
+ case 'data': {
1182
+ if (method === 'GET' || method === 'HEAD') {
1183
+ break;
1184
+ }
1185
+ // body data:
1186
+ // express, koa, nextjs: req.body
1187
+ //
1188
+ // when using node by itself, you have to read the incoming stream(see
1189
+ // https://nodejs.dev/learn/get-http-request-body-data-using-nodejs); if a user is doing that, we can't know
1190
+ // where they're going to store the final result, so they'll have to capture this data themselves
1191
+ if (req.body !== undefined) {
1192
+ requestData.data = isString(req.body) ? req.body : JSON.stringify(normalize(req.body));
1193
+ }
1194
+ break;
1195
+ }
1196
+ default: {
1197
+ if ({}.hasOwnProperty.call(req, key)) {
1198
+ requestData[key] = (req )[key];
1199
+ }
1200
+ }
1201
+ }
1202
+ });
1203
+
1204
+ return requestData;
1205
+ }
1206
+
1207
+ /**
1208
+ * Add data from the given request to the given event
1209
+ *
1210
+ * @param event The event to which the request data will be added
1211
+ * @param req Request object
1212
+ * @param options.include Flags to control what data is included
1213
+ * @param options.deps Injected platform-specific dependencies
1214
+ * @returns The mutated `Event` object
1215
+ */
1216
+ function addRequestDataToEvent(
1217
+ event,
1218
+ req,
1219
+ options,
1220
+ ) {
1221
+ const include = {
1222
+ ...DEFAULT_INCLUDES,
1223
+ ...(options && options.include),
1224
+ };
1225
+
1226
+ if (include.request) {
1227
+ const extractedRequestData = Array.isArray(include.request)
1228
+ ? extractRequestData(req, { include: include.request })
1229
+ : extractRequestData(req);
1230
+
1231
+ event.request = {
1232
+ ...event.request,
1233
+ ...extractedRequestData,
1234
+ };
1235
+ }
1236
+
1237
+ if (include.user) {
1238
+ const extractedUser = req.user && isPlainObject(req.user) ? extractUserData(req.user, include.user) : {};
1239
+
1240
+ if (Object.keys(extractedUser).length) {
1241
+ event.user = {
1242
+ ...event.user,
1243
+ ...extractedUser,
1244
+ };
1245
+ }
1246
+ }
1247
+
1248
+ // client ip:
1249
+ // node, nextjs: req.socket.remoteAddress
1250
+ // express, koa: req.ip
1251
+ if (include.ip) {
1252
+ const ip = req.ip || (req.socket && req.socket.remoteAddress);
1253
+ if (ip) {
1254
+ event.user = {
1255
+ ...event.user,
1256
+ ip_address: ip,
316
1257
  };
317
1258
  }
318
- } catch (_) {
319
- // some runtimes can crash invoking crypto
320
- // https://github.com/getsentry/sentry-javascript/issues/8935
321
1259
  }
322
1260
 
323
- // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/2117523#2117523
324
- // Concatenating the following numbers as strings results in '10000000100040008000100000000000'
325
- return (([1e7] ) + 1e3 + 4e3 + 8e3 + 1e11).replace(/[018]/g, c =>
326
- // eslint-disable-next-line no-bitwise
327
- ((c ) ^ ((getRandomByte() & 15) >> ((c ) / 4))).toString(16),
328
- );
1261
+ if (include.transaction && !event.transaction) {
1262
+ // TODO do we even need this anymore?
1263
+ // TODO make this work for nextjs
1264
+ event.transaction = extractTransaction(req, include.transaction);
1265
+ }
1266
+
1267
+ return event;
1268
+ }
1269
+
1270
+ function extractQueryParams(req) {
1271
+ // url (including path and query string):
1272
+ // node, express: req.originalUrl
1273
+ // koa, nextjs: req.url
1274
+ let originalUrl = req.originalUrl || req.url || '';
1275
+
1276
+ if (!originalUrl) {
1277
+ return;
1278
+ }
1279
+
1280
+ // The `URL` constructor can't handle internal URLs of the form `/some/path/here`, so stick a dummy protocol and
1281
+ // hostname on the beginning. Since the point here is just to grab the query string, it doesn't matter what we use.
1282
+ if (originalUrl.startsWith('/')) {
1283
+ originalUrl = `http://dogs.are.great${originalUrl}`;
1284
+ }
1285
+
1286
+ try {
1287
+ const queryParams = req.query || new URL(originalUrl).search.slice(1);
1288
+ return queryParams.length ? queryParams : undefined;
1289
+ } catch (e2) {
1290
+ return undefined;
1291
+ }
329
1292
  }
330
1293
 
331
1294
  const ONE_SECOND_IN_MS = 1000;
@@ -433,15 +1396,6 @@ const timestampInSeconds = createUnixTimestampInSecondsFunc();
433
1396
  return dateNow;
434
1397
  })();
435
1398
 
436
- /**
437
- * Creates an envelope.
438
- * Make sure to always explicitly provide the generic to this function
439
- * so that the envelope types resolve correctly.
440
- */
441
- function createEnvelope(headers, items = []) {
442
- return [headers, items] ;
443
- }
444
-
445
1399
  /**
446
1400
  * Convenience function to loop through the items and item types of an envelope.
447
1401
  * (This function was mostly created because working with envelope types is painful at the moment)
@@ -466,6 +1420,13 @@ function forEachEnvelopeItem(
466
1420
  return false;
467
1421
  }
468
1422
 
1423
+ /**
1424
+ * This serves as a build time flag that will be true by default, but false in non-debug builds or if users replace `__SENTRY_DEBUG__` in their generated code.
1425
+ *
1426
+ * ATTENTION: This constant must never cross package boundaries (i.e. be exported) to guarantee that it can be used for tree shaking.
1427
+ */
1428
+ const DEBUG_BUILD$1 = (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__);
1429
+
469
1430
  /**
470
1431
  * @private Private API with no semver guarantees!
471
1432
  *
@@ -495,13 +1456,6 @@ function getSentryCarrier(carrier) {
495
1456
  return carrier.__SENTRY__;
496
1457
  }
497
1458
 
498
- /**
499
- * This serves as a build time flag that will be true by default, but false in non-debug builds or if users replace `__SENTRY_DEBUG__` in their generated code.
500
- *
501
- * ATTENTION: This constant must never cross package boundaries (i.e. be exported) to guarantee that it can be used for tree shaking.
502
- */
503
- const DEBUG_BUILD$1 = (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__);
504
-
505
1459
  const DEFAULT_ENVIRONMENT = 'production';
506
1460
 
507
1461
  /**
@@ -700,6 +1654,9 @@ class Scope {
700
1654
 
701
1655
  /**
702
1656
  * Transaction Name
1657
+ *
1658
+ * IMPORTANT: The transaction name on the scope has nothing to do with root spans/transaction objects.
1659
+ * It's purpose is to assign a transaction to the scope that's added to non-transaction events.
703
1660
  */
704
1661
 
705
1662
  /** Span */
@@ -735,7 +1692,7 @@ class Scope {
735
1692
  }
736
1693
 
737
1694
  /**
738
- * Clone this scope instance.
1695
+ * @inheritDoc
739
1696
  */
740
1697
  clone() {
741
1698
  const newScope = new Scope();
@@ -759,23 +1716,22 @@ class Scope {
759
1716
  return newScope;
760
1717
  }
761
1718
 
762
- /** Update the client on the scope. */
1719
+ /**
1720
+ * @inheritDoc
1721
+ */
763
1722
  setClient(client) {
764
1723
  this._client = client;
765
1724
  }
766
1725
 
767
1726
  /**
768
- * Get the client assigned to this scope.
769
- *
770
- * It is generally recommended to use the global function `Sentry.getClient()` instead, unless you know what you are doing.
1727
+ * @inheritDoc
771
1728
  */
772
1729
  getClient() {
773
1730
  return this._client ;
774
1731
  }
775
1732
 
776
1733
  /**
777
- * Add internal on change listener. Used for sub SDKs that need to store the scope.
778
- * @hidden
1734
+ * @inheritDoc
779
1735
  */
780
1736
  addScopeListener(callback) {
781
1737
  this._scopeListeners.push(callback);
@@ -893,8 +1849,7 @@ class Scope {
893
1849
  }
894
1850
 
895
1851
  /**
896
- * Sets the transaction name on the scope for future events.
897
- * @deprecated Use extra or tags instead.
1852
+ * @inheritDoc
898
1853
  */
899
1854
  setTransactionName(name) {
900
1855
  this._transactionName = name;
@@ -1156,7 +2111,7 @@ class Scope {
1156
2111
  }
1157
2112
 
1158
2113
  /**
1159
- * Add data which will be accessible during event processing but won't get sent to Sentry
2114
+ * @inheritDoc
1160
2115
  */
1161
2116
  setSDKProcessingMetadata(newData) {
1162
2117
  this._sdkProcessingMetadata = { ...this._sdkProcessingMetadata, ...newData };
@@ -1180,11 +2135,7 @@ class Scope {
1180
2135
  }
1181
2136
 
1182
2137
  /**
1183
- * Capture an exception for this scope.
1184
- *
1185
- * @param exception The exception to capture.
1186
- * @param hint Optinal additional data to attach to the Sentry event.
1187
- * @returns the id of the captured Sentry event.
2138
+ * @inheritDoc
1188
2139
  */
1189
2140
  captureException(exception, hint) {
1190
2141
  const eventId = hint && hint.event_id ? hint.event_id : uuid4();
@@ -1211,12 +2162,7 @@ class Scope {
1211
2162
  }
1212
2163
 
1213
2164
  /**
1214
- * Capture a message for this scope.
1215
- *
1216
- * @param message The message to capture.
1217
- * @param level An optional severity level to report the message with.
1218
- * @param hint Optional additional data to attach to the Sentry event.
1219
- * @returns the id of the captured message.
2165
+ * @inheritDoc
1220
2166
  */
1221
2167
  captureMessage(message, level, hint) {
1222
2168
  const eventId = hint && hint.event_id ? hint.event_id : uuid4();
@@ -1244,11 +2190,7 @@ class Scope {
1244
2190
  }
1245
2191
 
1246
2192
  /**
1247
- * Captures a manually created event for this scope and sends it to Sentry.
1248
- *
1249
- * @param exception The event to capture.
1250
- * @param hint Optional additional data to attach to the Sentry event.
1251
- * @returns the id of the captured event.
2193
+ * @inheritDoc
1252
2194
  */
1253
2195
  captureEvent(event, hint) {
1254
2196
  const eventId = hint && hint.event_id ? hint.event_id : uuid4();
@@ -1287,7 +2229,7 @@ function generatePropagationContext() {
1287
2229
  };
1288
2230
  }
1289
2231
 
1290
- const SDK_VERSION = '8.0.0-alpha.2';
2232
+ const SDK_VERSION = '8.0.0-alpha.3';
1291
2233
 
1292
2234
  /**
1293
2235
  * API compatibility version of this hub.
@@ -1303,7 +2245,7 @@ const API_VERSION = parseFloat(SDK_VERSION);
1303
2245
  * Default maximum number of breadcrumbs added to an event. Can be overwritten
1304
2246
  * with {@link Options.maxBreadcrumbs}.
1305
2247
  */
1306
- const DEFAULT_BREADCRUMBS = 100;
2248
+ const DEFAULT_BREADCRUMBS$1 = 100;
1307
2249
 
1308
2250
  /**
1309
2251
  * A layer in the process stack.
@@ -1599,7 +2541,7 @@ class Hub {
1599
2541
 
1600
2542
  if (!client) return;
1601
2543
 
1602
- const { beforeBreadcrumb = null, maxBreadcrumbs = DEFAULT_BREADCRUMBS } =
2544
+ const { beforeBreadcrumb = null, maxBreadcrumbs = DEFAULT_BREADCRUMBS$1 } =
1603
2545
  (client.getOptions && client.getOptions()) || {};
1604
2546
 
1605
2547
  if (maxBreadcrumbs <= 0) return;
@@ -1690,46 +2632,6 @@ class Hub {
1690
2632
  }
1691
2633
  }
1692
2634
 
1693
- /**
1694
- * Starts a new `Transaction` and returns it. This is the entry point to manual tracing instrumentation.
1695
- *
1696
- * A tree structure can be built by adding child spans to the transaction, and child spans to other spans. To start a
1697
- * new child span within the transaction or any span, call the respective `.startChild()` method.
1698
- *
1699
- * Every child span must be finished before the transaction is finished, otherwise the unfinished spans are discarded.
1700
- *
1701
- * The transaction must be finished with a call to its `.end()` method, at which point the transaction with all its
1702
- * finished child spans will be sent to Sentry.
1703
- *
1704
- * @param context Properties of the new `Transaction`.
1705
- * @param customSamplingContext Information given to the transaction sampling function (along with context-dependent
1706
- * default values). See {@link Options.tracesSampler}.
1707
- *
1708
- * @returns The transaction which was just started
1709
- *
1710
- * @deprecated Use `startSpan()`, `startSpanManual()` or `startInactiveSpan()` instead.
1711
- */
1712
- startTransaction(context, customSamplingContext) {
1713
- const result = this._callExtensionMethod('startTransaction', context, customSamplingContext);
1714
-
1715
- if (DEBUG_BUILD$1 && !result) {
1716
- // eslint-disable-next-line deprecation/deprecation
1717
- const client = this.getClient();
1718
- if (!client) {
1719
- logger.warn(
1720
- "Tracing extension 'startTransaction' is missing. You should 'init' the SDK before calling 'startTransaction'",
1721
- );
1722
- } else {
1723
- logger.warn(`Tracing extension 'startTransaction' has not been added. Call 'addTracingExtensions' before calling 'init':
1724
- Sentry.addTracingExtensions();
1725
- Sentry.init({...});
1726
- `);
1727
- }
1728
- }
1729
-
1730
- return result;
1731
- }
1732
-
1733
2635
  /**
1734
2636
  * @inheritDoc
1735
2637
  *
@@ -1857,128 +2759,635 @@ function getCurrentHub() {
1857
2759
  return acs.getCurrentHub() || getGlobalHub();
1858
2760
  }
1859
2761
 
1860
- let defaultCurrentScope;
1861
- let defaultIsolationScope;
2762
+ /** Get the default current scope. */
2763
+ function getDefaultCurrentScope() {
2764
+ return getGlobalSingleton('defaultCurrentScope', () => new Scope());
2765
+ }
2766
+
2767
+ /** Get the default isolation scope. */
2768
+ function getDefaultIsolationScope() {
2769
+ return getGlobalSingleton('defaultIsolationScope', () => new Scope());
2770
+ }
2771
+
2772
+ /**
2773
+ * Get the global hub.
2774
+ * This will be removed during the v8 cycle and is only here to make migration easier.
2775
+ */
2776
+ function getGlobalHub() {
2777
+ const registry = getMainCarrier();
2778
+ const sentry = getSentryCarrier(registry) ;
2779
+
2780
+ // If there's no hub, or its an old API, assign a new one
2781
+ if (sentry.hub) {
2782
+ return sentry.hub;
2783
+ }
2784
+
2785
+ // eslint-disable-next-line deprecation/deprecation
2786
+ sentry.hub = new Hub(undefined, getDefaultCurrentScope(), getDefaultIsolationScope());
2787
+ return sentry.hub;
2788
+ }
2789
+
2790
+ /**
2791
+ * Get the current async context strategy.
2792
+ * If none has been setup, the default will be used.
2793
+ */
2794
+ function getAsyncContextStrategy(carrier) {
2795
+ const sentry = getSentryCarrier(carrier);
2796
+
2797
+ if (sentry.acs) {
2798
+ return sentry.acs;
2799
+ }
2800
+
2801
+ // Otherwise, use the default one
2802
+ return getHubStackAsyncContextStrategy();
2803
+ }
2804
+
2805
+ function withScope$1(callback) {
2806
+ // eslint-disable-next-line deprecation/deprecation
2807
+ return getGlobalHub().withScope(callback);
2808
+ }
2809
+
2810
+ function withSetScope(scope, callback) {
2811
+ const hub = getGlobalHub() ;
2812
+ // eslint-disable-next-line deprecation/deprecation
2813
+ return hub.withScope(() => {
2814
+ // eslint-disable-next-line deprecation/deprecation
2815
+ hub.getStackTop().scope = scope ;
2816
+ return callback(scope);
2817
+ });
2818
+ }
2819
+
2820
+ function withIsolationScope$1(callback) {
2821
+ // eslint-disable-next-line deprecation/deprecation
2822
+ return getGlobalHub().withScope(() => {
2823
+ // eslint-disable-next-line deprecation/deprecation
2824
+ return callback(getGlobalHub().getIsolationScope());
2825
+ });
2826
+ }
2827
+
2828
+ /* eslint-disable deprecation/deprecation */
2829
+ function getHubStackAsyncContextStrategy() {
2830
+ return {
2831
+ getCurrentHub: getGlobalHub,
2832
+ withIsolationScope: withIsolationScope$1,
2833
+ withScope: withScope$1,
2834
+ withSetScope,
2835
+ withSetIsolationScope: (_isolationScope, callback) => {
2836
+ return withIsolationScope$1(callback);
2837
+ },
2838
+ getCurrentScope: () => getGlobalHub().getScope(),
2839
+ getIsolationScope: () => getGlobalHub().getIsolationScope(),
2840
+ };
2841
+ }
2842
+ /* eslint-enable deprecation/deprecation */
2843
+
2844
+ /**
2845
+ * Get the currently active scope.
2846
+ */
2847
+ function getCurrentScope() {
2848
+ const carrier = getMainCarrier();
2849
+ const acs = getAsyncContextStrategy(carrier);
2850
+ return acs.getCurrentScope();
2851
+ }
2852
+
2853
+ /**
2854
+ * Get the currently active client.
2855
+ */
2856
+ function getClient() {
2857
+ return getCurrentScope().getClient();
2858
+ }
2859
+
2860
+ /**
2861
+ * Fetches the metric summary if it exists for the passed span
2862
+ */
2863
+ function getMetricSummaryJsonForSpan(span) {
2864
+
2865
+ {
2866
+ return undefined;
2867
+ }
2868
+ }
2869
+
2870
+ /**
2871
+ * Use this attribute to represent the operation of a span.
2872
+ */
2873
+ const SEMANTIC_ATTRIBUTE_SENTRY_OP = 'sentry.op';
2874
+
2875
+ /**
2876
+ * Use this attribute to represent the origin of a span.
2877
+ */
2878
+ const SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN = 'sentry.origin';
2879
+
2880
+ const SPAN_STATUS_UNSET = 0;
2881
+ const SPAN_STATUS_OK = 1;
2882
+ const TRACE_FLAG_SAMPLED = 0x1;
2883
+
2884
+ /**
2885
+ * Convert a span time input intp a timestamp in seconds.
2886
+ */
2887
+ function spanTimeInputToSeconds(input) {
2888
+ if (typeof input === 'number') {
2889
+ return ensureTimestampInSeconds(input);
2890
+ }
2891
+
2892
+ if (Array.isArray(input)) {
2893
+ // See {@link HrTime} for the array-based time format
2894
+ return input[0] + input[1] / 1e9;
2895
+ }
2896
+
2897
+ if (input instanceof Date) {
2898
+ return ensureTimestampInSeconds(input.getTime());
2899
+ }
2900
+
2901
+ return timestampInSeconds();
2902
+ }
2903
+
2904
+ /**
2905
+ * Converts a timestamp to second, if it was in milliseconds, or keeps it as second.
2906
+ */
2907
+ function ensureTimestampInSeconds(timestamp) {
2908
+ const isMs = timestamp > 9999999999;
2909
+ return isMs ? timestamp / 1000 : timestamp;
2910
+ }
2911
+
2912
+ /**
2913
+ * Convert a span to a JSON representation.
2914
+ * Note that all fields returned here are optional and need to be guarded against.
2915
+ *
2916
+ * Note: Because of this, we currently have a circular type dependency (which we opted out of in package.json).
2917
+ * This is not avoidable as we need `spanToJSON` in `spanUtils.ts`, which in turn is needed by `span.ts` for backwards compatibility.
2918
+ * And `spanToJSON` needs the Span class from `span.ts` to check here.
2919
+ */
2920
+ function spanToJSON(span) {
2921
+ if (spanIsSentrySpan(span)) {
2922
+ return span.getSpanJSON();
2923
+ }
2924
+
2925
+ try {
2926
+ const { spanId: span_id, traceId: trace_id } = span.spanContext();
2927
+
2928
+ // Handle a span from @opentelemetry/sdk-base-trace's `Span` class
2929
+ if (spanIsOpenTelemetrySdkTraceBaseSpan(span)) {
2930
+ const { attributes, startTime, name, endTime, parentSpanId, status } = span;
2931
+
2932
+ return dropUndefinedKeys({
2933
+ span_id,
2934
+ trace_id,
2935
+ data: attributes,
2936
+ description: name,
2937
+ parent_span_id: parentSpanId,
2938
+ start_timestamp: spanTimeInputToSeconds(startTime),
2939
+ // This is [0,0] by default in OTEL, in which case we want to interpret this as no end time
2940
+ timestamp: spanTimeInputToSeconds(endTime) || undefined,
2941
+ status: getStatusMessage(status),
2942
+ op: attributes[SEMANTIC_ATTRIBUTE_SENTRY_OP],
2943
+ origin: attributes[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN] ,
2944
+ _metrics_summary: getMetricSummaryJsonForSpan(span),
2945
+ });
2946
+ }
2947
+
2948
+ // Finally, at least we have `spanContext()`....
2949
+ return {
2950
+ span_id,
2951
+ trace_id,
2952
+ };
2953
+ } catch (e) {
2954
+ return {};
2955
+ }
2956
+ }
2957
+
2958
+ function spanIsOpenTelemetrySdkTraceBaseSpan(span) {
2959
+ const castSpan = span ;
2960
+ return !!castSpan.attributes && !!castSpan.startTime && !!castSpan.name && !!castSpan.endTime && !!castSpan.status;
2961
+ }
2962
+
2963
+ /** Exported only for tests. */
2964
+
2965
+ /**
2966
+ * Sadly, due to circular dependency checks we cannot actually import the Span class here and check for instanceof.
2967
+ * :( So instead we approximate this by checking if it has the `getSpanJSON` method.
2968
+ */
2969
+ function spanIsSentrySpan(span) {
2970
+ return typeof (span ).getSpanJSON === 'function';
2971
+ }
2972
+
2973
+ /**
2974
+ * Returns true if a span is sampled.
2975
+ * In most cases, you should just use `span.isRecording()` instead.
2976
+ * However, this has a slightly different semantic, as it also returns false if the span is finished.
2977
+ * So in the case where this distinction is important, use this method.
2978
+ */
2979
+ function spanIsSampled(span) {
2980
+ // We align our trace flags with the ones OpenTelemetry use
2981
+ // So we also check for sampled the same way they do.
2982
+ const { traceFlags } = span.spanContext();
2983
+ return traceFlags === TRACE_FLAG_SAMPLED;
2984
+ }
2985
+
2986
+ /** Get the status message to use for a JSON representation of a span. */
2987
+ function getStatusMessage(status) {
2988
+ if (!status || status.code === SPAN_STATUS_UNSET) {
2989
+ return undefined;
2990
+ }
2991
+
2992
+ if (status.code === SPAN_STATUS_OK) {
2993
+ return 'ok';
2994
+ }
2995
+
2996
+ return status.message || 'unknown_error';
2997
+ }
2998
+ const ROOT_SPAN_FIELD = '_sentryRootSpan';
2999
+
3000
+ /**
3001
+ * Returns the root span of a given span.
3002
+ */
3003
+ function getRootSpan(span) {
3004
+ return span[ROOT_SPAN_FIELD] || span;
3005
+ }
3006
+
3007
+ /**
3008
+ * Convert a new integration function to the legacy class syntax.
3009
+ * In v8, we can remove this and instead export the integration functions directly.
3010
+ *
3011
+ * @deprecated This will be removed in v8!
3012
+ */
3013
+ function convertIntegrationFnToClass(
3014
+ name,
3015
+ fn,
3016
+ ) {
3017
+ return Object.assign(
3018
+ function ConvertedIntegration(...args) {
3019
+ return fn(...args);
3020
+ },
3021
+ { id: name },
3022
+ ) ;
3023
+ }
3024
+
3025
+ /**
3026
+ * Define an integration function that can be used to create an integration instance.
3027
+ * Note that this by design hides the implementation details of the integration, as they are considered internal.
3028
+ */
3029
+ function defineIntegration(fn) {
3030
+ return fn;
3031
+ }
3032
+
3033
+ /** Keys are source filename/url, values are metadata objects. */
3034
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
3035
+ const filenameMetadataMap = new Map();
3036
+ /** Set of stack strings that have already been parsed. */
3037
+ const parsedStacks = new Set();
1862
3038
 
1863
- /** Get the default current scope. */
1864
- function getDefaultCurrentScope() {
1865
- if (!defaultCurrentScope) {
1866
- defaultCurrentScope = new Scope();
3039
+ function ensureMetadataStacksAreParsed(parser) {
3040
+ if (!GLOBAL_OBJ._sentryModuleMetadata) {
3041
+ return;
1867
3042
  }
1868
3043
 
1869
- return defaultCurrentScope;
1870
- }
3044
+ for (const stack of Object.keys(GLOBAL_OBJ._sentryModuleMetadata)) {
3045
+ const metadata = GLOBAL_OBJ._sentryModuleMetadata[stack];
1871
3046
 
1872
- /** Get the default isolation scope. */
1873
- function getDefaultIsolationScope() {
1874
- if (!defaultIsolationScope) {
1875
- defaultIsolationScope = new Scope();
3047
+ if (parsedStacks.has(stack)) {
3048
+ continue;
3049
+ }
3050
+
3051
+ // Ensure this stack doesn't get parsed again
3052
+ parsedStacks.add(stack);
3053
+
3054
+ const frames = parser(stack);
3055
+
3056
+ // Go through the frames starting from the top of the stack and find the first one with a filename
3057
+ for (const frame of frames.reverse()) {
3058
+ if (frame.filename) {
3059
+ // Save the metadata for this filename
3060
+ filenameMetadataMap.set(frame.filename, metadata);
3061
+ break;
3062
+ }
3063
+ }
1876
3064
  }
3065
+ }
1877
3066
 
1878
- return defaultIsolationScope;
3067
+ /**
3068
+ * Retrieve metadata for a specific JavaScript file URL.
3069
+ *
3070
+ * Metadata is injected by the Sentry bundler plugins using the `_experiments.moduleMetadata` config option.
3071
+ */
3072
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
3073
+ function getMetadataForUrl(parser, filename) {
3074
+ ensureMetadataStacksAreParsed(parser);
3075
+ return filenameMetadataMap.get(filename);
1879
3076
  }
1880
3077
 
1881
3078
  /**
1882
- * Get the global hub.
1883
- * This will be removed during the v8 cycle and is only here to make migration easier.
3079
+ * Adds metadata to stack frames.
3080
+ *
3081
+ * Metadata is injected by the Sentry bundler plugins using the `_experiments.moduleMetadata` config option.
1884
3082
  */
1885
- function getGlobalHub() {
1886
- const registry = getMainCarrier();
1887
- const sentry = getSentryCarrier(registry) ;
3083
+ function addMetadataToStackFrames(parser, event) {
3084
+ try {
3085
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
3086
+ event.exception.values.forEach(exception => {
3087
+ if (!exception.stacktrace) {
3088
+ return;
3089
+ }
1888
3090
 
1889
- // If there's no hub, or its an old API, assign a new one
1890
- if (sentry.hub) {
1891
- return sentry.hub;
1892
- }
3091
+ for (const frame of exception.stacktrace.frames || []) {
3092
+ if (!frame.filename) {
3093
+ continue;
3094
+ }
1893
3095
 
1894
- // eslint-disable-next-line deprecation/deprecation
1895
- sentry.hub = new Hub(undefined, getDefaultCurrentScope(), getDefaultIsolationScope());
1896
- return sentry.hub;
3096
+ const metadata = getMetadataForUrl(parser, frame.filename);
3097
+
3098
+ if (metadata) {
3099
+ frame.module_metadata = metadata;
3100
+ }
3101
+ }
3102
+ });
3103
+ } catch (_) {
3104
+ // To save bundle size we're just try catching here instead of checking for the existence of all the different objects.
3105
+ }
1897
3106
  }
1898
3107
 
1899
3108
  /**
1900
- * Get the current async context strategy.
1901
- * If none has been setup, the default will be used.
3109
+ * Strips metadata from stack frames.
1902
3110
  */
1903
- function getAsyncContextStrategy(carrier) {
1904
- const sentry = getSentryCarrier(carrier);
3111
+ function stripMetadataFromStackFrames(event) {
3112
+ try {
3113
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
3114
+ event.exception.values.forEach(exception => {
3115
+ if (!exception.stacktrace) {
3116
+ return;
3117
+ }
1905
3118
 
1906
- if (sentry.acs) {
1907
- return sentry.acs;
3119
+ for (const frame of exception.stacktrace.frames || []) {
3120
+ delete frame.module_metadata;
3121
+ }
3122
+ });
3123
+ } catch (_) {
3124
+ // To save bundle size we're just try catching here instead of checking for the existence of all the different objects.
1908
3125
  }
1909
-
1910
- // Otherwise, use the default one
1911
- return getHubStackAsyncContextStrategy();
1912
3126
  }
1913
3127
 
1914
- function withScope(callback) {
1915
- // eslint-disable-next-line deprecation/deprecation
1916
- return getGlobalHub().withScope(callback);
1917
- }
3128
+ const INTEGRATION_NAME$a = 'ModuleMetadata';
1918
3129
 
1919
- function withSetScope(scope, callback) {
1920
- const hub = getGlobalHub() ;
1921
- // eslint-disable-next-line deprecation/deprecation
1922
- return hub.withScope(() => {
1923
- // eslint-disable-next-line deprecation/deprecation
1924
- hub.getStackTop().scope = scope ;
1925
- return callback(scope);
1926
- });
1927
- }
3130
+ const _moduleMetadataIntegration = (() => {
3131
+ return {
3132
+ name: INTEGRATION_NAME$a,
3133
+ setup(client) {
3134
+ // We need to strip metadata from stack frames before sending them to Sentry since these are client side only.
3135
+ client.on('beforeEnvelope', envelope => {
3136
+ forEachEnvelopeItem(envelope, (item, type) => {
3137
+ if (type === 'event') {
3138
+ const event = Array.isArray(item) ? (item )[1] : undefined;
3139
+
3140
+ if (event) {
3141
+ stripMetadataFromStackFrames(event);
3142
+ item[1] = event;
3143
+ }
3144
+ }
3145
+ });
3146
+ });
3147
+ },
1928
3148
 
1929
- function withIsolationScope(callback) {
1930
- // eslint-disable-next-line deprecation/deprecation
1931
- return getGlobalHub().withScope(() => {
1932
- // eslint-disable-next-line deprecation/deprecation
1933
- return callback(getGlobalHub().getIsolationScope());
1934
- });
1935
- }
3149
+ processEvent(event, _hint, client) {
3150
+ const stackParser = client.getOptions().stackParser;
3151
+ addMetadataToStackFrames(stackParser, event);
3152
+ return event;
3153
+ },
3154
+ };
3155
+ }) ;
3156
+
3157
+ const moduleMetadataIntegration = defineIntegration(_moduleMetadataIntegration);
3158
+
3159
+ /**
3160
+ * Adds module metadata to stack frames.
3161
+ *
3162
+ * Metadata can be injected by the Sentry bundler plugins using the `_experiments.moduleMetadata` config option.
3163
+ *
3164
+ * When this integration is added, the metadata passed to the bundler plugin is added to the stack frames of all events
3165
+ * under the `module_metadata` property. This can be used to help in tagging or routing of events from different teams
3166
+ * our sources
3167
+ *
3168
+ * @deprecated Use `moduleMetadataIntegration()` instead.
3169
+ */
3170
+ // eslint-disable-next-line deprecation/deprecation
3171
+ convertIntegrationFnToClass(
3172
+ INTEGRATION_NAME$a,
3173
+ moduleMetadataIntegration,
3174
+ )
3175
+
3176
+ ;
3177
+
3178
+ const DEFAULT_OPTIONS = {
3179
+ include: {
3180
+ cookies: true,
3181
+ data: true,
3182
+ headers: true,
3183
+ ip: false,
3184
+ query_string: true,
3185
+ url: true,
3186
+ user: {
3187
+ id: true,
3188
+ username: true,
3189
+ email: true,
3190
+ },
3191
+ },
3192
+ transactionNamingScheme: 'methodPath' ,
3193
+ };
3194
+
3195
+ const INTEGRATION_NAME$9 = 'RequestData';
3196
+
3197
+ const _requestDataIntegration = ((options = {}) => {
3198
+ const _options = {
3199
+ ...DEFAULT_OPTIONS,
3200
+ ...options,
3201
+ include: {
3202
+ ...DEFAULT_OPTIONS.include,
3203
+ ...options.include,
3204
+ user:
3205
+ options.include && typeof options.include.user === 'boolean'
3206
+ ? options.include.user
3207
+ : {
3208
+ ...DEFAULT_OPTIONS.include.user,
3209
+ // Unclear why TS still thinks `options.include.user` could be a boolean at this point
3210
+ ...((options.include || {}).user ),
3211
+ },
3212
+ },
3213
+ };
1936
3214
 
1937
- /* eslint-disable deprecation/deprecation */
1938
- function getHubStackAsyncContextStrategy() {
1939
3215
  return {
1940
- getCurrentHub: getGlobalHub,
1941
- withIsolationScope,
1942
- withScope,
1943
- withSetScope,
1944
- withSetIsolationScope: (_isolationScope, callback) => {
1945
- return withIsolationScope(callback);
3216
+ name: INTEGRATION_NAME$9,
3217
+ processEvent(event, _hint, client) {
3218
+ // Note: In the long run, most of the logic here should probably move into the request data utility functions. For
3219
+ // the moment it lives here, though, until https://github.com/getsentry/sentry-javascript/issues/5718 is addressed.
3220
+ // (TL;DR: Those functions touch many parts of the repo in many different ways, and need to be clened up. Once
3221
+ // that's happened, it will be easier to add this logic in without worrying about unexpected side effects.)
3222
+ const { transactionNamingScheme } = _options;
3223
+
3224
+ const { sdkProcessingMetadata = {} } = event;
3225
+ const req = sdkProcessingMetadata.request;
3226
+
3227
+ if (!req) {
3228
+ return event;
3229
+ }
3230
+
3231
+ const addRequestDataOptions = convertReqDataIntegrationOptsToAddReqDataOpts(_options);
3232
+
3233
+ const processedEvent = addRequestDataToEvent(event, req, addRequestDataOptions);
3234
+
3235
+ // Transaction events already have the right `transaction` value
3236
+ if (event.type === 'transaction' || transactionNamingScheme === 'handler') {
3237
+ return processedEvent;
3238
+ }
3239
+
3240
+ // In all other cases, use the request's associated transaction (if any) to overwrite the event's `transaction`
3241
+ // value with a high-quality one
3242
+ const reqWithTransaction = req ;
3243
+ const transaction = reqWithTransaction._sentryTransaction;
3244
+ if (transaction) {
3245
+ const name = spanToJSON(transaction).description || '';
3246
+
3247
+ // TODO (v8): Remove the nextjs check and just base it on `transactionNamingScheme` for all SDKs. (We have to
3248
+ // keep it the way it is for the moment, because changing the names of transactions in Sentry has the potential
3249
+ // to break things like alert rules.)
3250
+ const shouldIncludeMethodInTransactionName =
3251
+ getSDKName(client) === 'sentry.javascript.nextjs'
3252
+ ? name.startsWith('/api')
3253
+ : transactionNamingScheme !== 'path';
3254
+
3255
+ const [transactionValue] = extractPathForTransaction(req, {
3256
+ path: true,
3257
+ method: shouldIncludeMethodInTransactionName,
3258
+ customRoute: name,
3259
+ });
3260
+
3261
+ processedEvent.transaction = transactionValue;
3262
+ }
3263
+
3264
+ return processedEvent;
1946
3265
  },
1947
- getCurrentScope: () => getGlobalHub().getScope(),
1948
- getIsolationScope: () => getGlobalHub().getIsolationScope(),
1949
3266
  };
1950
- }
3267
+ }) ;
3268
+
3269
+ const requestDataIntegration = defineIntegration(_requestDataIntegration);
1951
3270
 
1952
3271
  /**
1953
- * Convert a span to a JSON representation.
1954
- * Note that all fields returned here are optional and need to be guarded against.
1955
- *
1956
- * Note: Because of this, we currently have a circular type dependency (which we opted out of in package.json).
1957
- * This is not avoidable as we need `spanToJSON` in `spanUtils.ts`, which in turn is needed by `span.ts` for backwards compatibility.
1958
- * And `spanToJSON` needs the Span class from `span.ts` to check here.
3272
+ * Add data about a request to an event. Primarily for use in Node-based SDKs, but included in `@sentry/core`
3273
+ * so it can be used in cross-platform SDKs like `@sentry/nextjs`.
3274
+ * @deprecated Use `requestDataIntegration()` instead.
1959
3275
  */
1960
- function spanToJSON(span) {
1961
- if (spanIsSentrySpan(span)) {
1962
- return span.getSpanJSON();
3276
+ // eslint-disable-next-line deprecation/deprecation
3277
+ convertIntegrationFnToClass(INTEGRATION_NAME$9, requestDataIntegration)
3278
+
3279
+ ;
3280
+
3281
+ /** Convert this integration's options to match what `addRequestDataToEvent` expects */
3282
+ /** TODO: Can possibly be deleted once https://github.com/getsentry/sentry-javascript/issues/5718 is fixed */
3283
+ function convertReqDataIntegrationOptsToAddReqDataOpts(
3284
+ integrationOptions,
3285
+ ) {
3286
+ const {
3287
+ transactionNamingScheme,
3288
+ include: { ip, user, ...requestOptions },
3289
+ } = integrationOptions;
3290
+
3291
+ const requestIncludeKeys = ['method'];
3292
+ for (const [key, value] of Object.entries(requestOptions)) {
3293
+ if (value) {
3294
+ requestIncludeKeys.push(key);
3295
+ }
1963
3296
  }
1964
3297
 
1965
- // Fallback: We also check for `.toJSON()` here...
1966
- if (typeof (span ).toJSON === 'function') {
1967
- return (span ).toJSON();
3298
+ let addReqDataUserOpt;
3299
+ if (user === undefined) {
3300
+ addReqDataUserOpt = true;
3301
+ } else if (typeof user === 'boolean') {
3302
+ addReqDataUserOpt = user;
3303
+ } else {
3304
+ const userIncludeKeys = [];
3305
+ for (const [key, value] of Object.entries(user)) {
3306
+ if (value) {
3307
+ userIncludeKeys.push(key);
3308
+ }
3309
+ }
3310
+ addReqDataUserOpt = userIncludeKeys;
1968
3311
  }
1969
3312
 
1970
- // TODO: Also handle OTEL spans here!
3313
+ return {
3314
+ include: {
3315
+ ip,
3316
+ user: addReqDataUserOpt,
3317
+ request: requestIncludeKeys.length !== 0 ? requestIncludeKeys : undefined,
3318
+ transaction: transactionNamingScheme,
3319
+ },
3320
+ };
3321
+ }
1971
3322
 
1972
- return {};
3323
+ function getSDKName(client) {
3324
+ try {
3325
+ // For a long chain like this, it's fewer bytes to combine a try-catch with assuming everything is there than to
3326
+ // write out a long chain of `a && a.b && a.b.c && ...`
3327
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
3328
+ return client.getOptions()._metadata.sdk.name;
3329
+ } catch (err) {
3330
+ // In theory we should never get here
3331
+ return undefined;
3332
+ }
1973
3333
  }
1974
3334
 
3335
+ let originalFunctionToString;
3336
+
3337
+ const INTEGRATION_NAME$8 = 'FunctionToString';
3338
+
3339
+ const SETUP_CLIENTS = new WeakMap();
3340
+
3341
+ const _functionToStringIntegration = (() => {
3342
+ return {
3343
+ name: INTEGRATION_NAME$8,
3344
+ setupOnce() {
3345
+ // eslint-disable-next-line @typescript-eslint/unbound-method
3346
+ originalFunctionToString = Function.prototype.toString;
3347
+
3348
+ // intrinsics (like Function.prototype) might be immutable in some environments
3349
+ // e.g. Node with --frozen-intrinsics, XS (an embedded JavaScript engine) or SES (a JavaScript proposal)
3350
+ try {
3351
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
3352
+ Function.prototype.toString = function ( ...args) {
3353
+ const originalFunction = getOriginalFunction(this);
3354
+ const context =
3355
+ SETUP_CLIENTS.has(getClient() ) && originalFunction !== undefined ? originalFunction : this;
3356
+ return originalFunctionToString.apply(context, args);
3357
+ };
3358
+ } catch (e) {
3359
+ // ignore errors here, just don't patch this
3360
+ }
3361
+ },
3362
+ setup(client) {
3363
+ SETUP_CLIENTS.set(client, true);
3364
+ },
3365
+ };
3366
+ }) ;
3367
+
1975
3368
  /**
1976
- * Sadly, due to circular dependency checks we cannot actually import the Span class here and check for instanceof.
1977
- * :( So instead we approximate this by checking if it has the `getSpanJSON` method.
3369
+ * Patch toString calls to return proper name for wrapped functions.
3370
+ *
3371
+ * ```js
3372
+ * Sentry.init({
3373
+ * integrations: [
3374
+ * functionToStringIntegration(),
3375
+ * ],
3376
+ * });
3377
+ * ```
1978
3378
  */
1979
- function spanIsSentrySpan(span) {
1980
- return typeof (span ).getSpanJSON === 'function';
1981
- }
3379
+ const functionToStringIntegration = defineIntegration(_functionToStringIntegration);
3380
+
3381
+ /**
3382
+ * Patch toString calls to return proper name for wrapped functions.
3383
+ *
3384
+ * @deprecated Use `functionToStringIntegration()` instead.
3385
+ */
3386
+ // eslint-disable-next-line deprecation/deprecation
3387
+ convertIntegrationFnToClass(
3388
+ INTEGRATION_NAME$8,
3389
+ functionToStringIntegration,
3390
+ ) ;
1982
3391
 
1983
3392
  /**
1984
3393
  * This serves as a build time flag that will be true by default, but false in non-debug builds or if users replace `__SENTRY_DEBUG__` in their generated code.
@@ -2118,7 +3527,6 @@ const CpuProfilerBindings = {
2118
3527
  },
2119
3528
  };
2120
3529
 
2121
- /* eslint-disable max-lines */
2122
3530
  // We require the file because if we import it, it will be included in the bundle.
2123
3531
  // I guess tsc does not check file contents when it's imported.
2124
3532
  const THREAD_ID_STRING = String(worker_threads.threadId);
@@ -2164,84 +3572,6 @@ function enrichWithThreadInformation(profile) {
2164
3572
  },
2165
3573
  };
2166
3574
  }
2167
- /**
2168
- * Extract sdk info from from the API metadata
2169
- * @param {SdkMetadata | undefined} metadata
2170
- * @returns {SdkInfo | undefined}
2171
- */
2172
- function getSdkMetadataForEnvelopeHeader(metadata) {
2173
- if (!metadata || !metadata.sdk) {
2174
- return undefined;
2175
- }
2176
- return { name: metadata.sdk.name, version: metadata.sdk.version };
2177
- }
2178
- /**
2179
- * Apply SdkInfo (name, version, packages, integrations) to the corresponding event key.
2180
- * Merge with existing data if any.
2181
- *
2182
- * @param {Event} event
2183
- * @param {SdkInfo | undefined} sdkInfo
2184
- * @returns {Event}
2185
- */
2186
- function enhanceEventWithSdkInfo(event, sdkInfo) {
2187
- if (!sdkInfo) {
2188
- return event;
2189
- }
2190
- event.sdk = event.sdk || {};
2191
- event.sdk.name = event.sdk.name || sdkInfo.name || 'unknown sdk';
2192
- event.sdk.version = event.sdk.version || sdkInfo.version || 'unknown sdk version';
2193
- event.sdk.integrations = [...(event.sdk.integrations || []), ...(sdkInfo.integrations || [])];
2194
- event.sdk.packages = [...(event.sdk.packages || []), ...(sdkInfo.packages || [])];
2195
- return event;
2196
- }
2197
- /**
2198
- *
2199
- * @param {Event} event
2200
- * @param {SdkInfo | undefined} sdkInfo
2201
- * @param {string | undefined} tunnel
2202
- * @param {DsnComponents} dsn
2203
- * @returns {EventEnvelopeHeaders}
2204
- */
2205
- function createEventEnvelopeHeaders(event, sdkInfo, tunnel, dsn) {
2206
- const dynamicSamplingContext = event.sdkProcessingMetadata && event.sdkProcessingMetadata['dynamicSamplingContext'];
2207
- return Object.assign(Object.assign(Object.assign({ event_id: event.event_id, sent_at: new Date().toISOString() }, (sdkInfo && { sdk: sdkInfo })), (!!tunnel && { dsn: dsnToString(dsn) })), (event.type === 'transaction' &&
2208
- dynamicSamplingContext && {
2209
- trace: dropUndefinedKeys(Object.assign({}, dynamicSamplingContext)),
2210
- }));
2211
- }
2212
- /**
2213
- * Creates a profiling event envelope from a Sentry event. If profile does not pass
2214
- * validation, returns null.
2215
- * @param {Event}
2216
- * @returns {Profile | null}
2217
- */
2218
- function createProfilingEventFromTransaction(event) {
2219
- var _a, _b, _c, _d, _e, _f, _g;
2220
- if (event.type !== 'transaction') {
2221
- // createProfilingEventEnvelope should only be called for transactions,
2222
- // we type guard this behavior with isProfiledTransactionEvent.
2223
- throw new TypeError('Profiling events may only be attached to transactions, this should never occur.');
2224
- }
2225
- const rawProfile = event.sdkProcessingMetadata['profile'];
2226
- if (rawProfile === undefined || rawProfile === null) {
2227
- throw new TypeError(`Cannot construct profiling event envelope without a valid profile. Got ${rawProfile} instead.`);
2228
- }
2229
- if (!rawProfile.profile_id) {
2230
- throw new TypeError(`Cannot construct profiling event envelope without a valid profile id. Got ${rawProfile.profile_id} instead.`);
2231
- }
2232
- if (!isValidProfile(rawProfile)) {
2233
- return null;
2234
- }
2235
- return createProfilePayload(rawProfile, {
2236
- release: (_a = event.release) !== null && _a !== void 0 ? _a : '',
2237
- environment: (_b = event.environment) !== null && _b !== void 0 ? _b : '',
2238
- event_id: (_c = event.event_id) !== null && _c !== void 0 ? _c : '',
2239
- transaction: (_d = event.transaction) !== null && _d !== void 0 ? _d : '',
2240
- start_timestamp: event.start_timestamp ? event.start_timestamp * 1000 : Date.now(),
2241
- trace_id: (_g = (_f = (_e = event.contexts) === null || _e === void 0 ? void 0 : _e['trace']) === null || _f === void 0 ? void 0 : _f['trace_id']) !== null && _g !== void 0 ? _g : '',
2242
- profile_id: rawProfile.profile_id,
2243
- });
2244
- }
2245
3575
  /**
2246
3576
  * Creates a profiling envelope item, if the profile does not pass validation, returns null.
2247
3577
  * @param {RawThreadCpuProfile}
@@ -2314,54 +3644,6 @@ function createProfilePayload(cpuProfile, { release, environment, event_id, tran
2314
3644
  };
2315
3645
  return profile;
2316
3646
  }
2317
- /**
2318
- * Creates an envelope from a profiling event.
2319
- * @param {Event} Profile
2320
- * @param {DsnComponents} dsn
2321
- * @param {SdkMetadata} metadata
2322
- * @param {string|undefined} tunnel
2323
- * @returns {Envelope|null}
2324
- */
2325
- function createProfilingEventEnvelope(event, dsn, metadata, tunnel) {
2326
- const sdkInfo = getSdkMetadataForEnvelopeHeader(metadata);
2327
- enhanceEventWithSdkInfo(event, metadata && metadata.sdk);
2328
- const envelopeHeaders = createEventEnvelopeHeaders(event, sdkInfo, tunnel, dsn);
2329
- const profile = createProfilingEventFromTransaction(event);
2330
- if (!profile) {
2331
- return null;
2332
- }
2333
- const envelopeItem = [
2334
- {
2335
- type: 'profile',
2336
- },
2337
- // @ts-expect-error profile is not part of EventItem yet
2338
- profile,
2339
- ];
2340
- return createEnvelope(envelopeHeaders, [envelopeItem]);
2341
- }
2342
- /**
2343
- * Check if event metadata contains profile information
2344
- * @param {Event}
2345
- * @returns {boolean}
2346
- */
2347
- function isProfiledTransactionEvent(event) {
2348
- return !!(event.sdkProcessingMetadata && event.sdkProcessingMetadata['profile']);
2349
- }
2350
- /**
2351
- * Due to how profiles are attached to event metadata, we may sometimes want to remove them to ensure
2352
- * they are not processed by other Sentry integrations. This can be the case when we cannot construct a valid
2353
- * profile from the data we have or some of the mechanisms to send the event (Hub, Transport etc) are not available to us.
2354
- *
2355
- * @param {Event | ProfiledEvent} event
2356
- * @returns {Event}
2357
- */
2358
- function maybeRemoveProfileFromSdkMetadata(event) {
2359
- if (!isProfiledTransactionEvent(event)) {
2360
- return event;
2361
- }
2362
- delete event.sdkProcessingMetadata.profile;
2363
- return event;
2364
- }
2365
3647
  /**
2366
3648
  * Checks the given sample rate to make sure it is valid type and value (a boolean, or a number between 0 and 1).
2367
3649
  * @param {unknown} rate
@@ -2515,12 +3797,11 @@ const MAX_PROFILE_DURATION_MS = 30 * 1000;
2515
3797
  * Takes a transaction and determines if it should be profiled or not. If it should be profiled, it returns the
2516
3798
  * profile_id, otherwise returns undefined. Takes care of setting profile context on transaction as well
2517
3799
  */
2518
- function maybeProfileTransaction(client, transaction, customSamplingContext) {
3800
+ function maybeProfileSpan(client, span, customSamplingContext) {
2519
3801
  // profilesSampleRate is multiplied with tracesSampleRate to get the final sampling rate. We dont perform
2520
- // the actual multiplication to get the final rate, but we discard the profile if the transaction was sampled,
2521
- // so anything after this block from here is based on the transaction sampling.
2522
- // eslint-disable-next-line deprecation/deprecation
2523
- if (!transaction.sampled) {
3802
+ // the actual multiplication to get the final rate, but we discard the profile if the span was sampled,
3803
+ // so anything after this block from here is based on the span sampling.
3804
+ if (!spanIsSampled(span)) {
2524
3805
  return;
2525
3806
  }
2526
3807
  // Client and options are required for profiling
@@ -2537,8 +3818,13 @@ function maybeProfileTransaction(client, transaction, customSamplingContext) {
2537
3818
  let profilesSampleRate = options.profilesSampleRate;
2538
3819
  // Prefer sampler to sample rate if both are provided.
2539
3820
  if (typeof profilesSampler === 'function') {
2540
- // eslint-disable-next-line deprecation/deprecation
2541
- profilesSampleRate = profilesSampler(Object.assign({ transactionContext: transaction.toContext() }, customSamplingContext));
3821
+ const { description: spanName = '<unknown>', data } = spanToJSON(span);
3822
+ // We bail out early if that is not the case
3823
+ const parentSampled = true;
3824
+ profilesSampleRate = profilesSampler(Object.assign({ name: spanName, attributes: data, transactionContext: {
3825
+ name: spanName,
3826
+ parentSampled,
3827
+ }, parentSampled }, customSamplingContext));
2542
3828
  }
2543
3829
  // Since this is coming from the user (or from a function provided by the user), who knows what we might get. (The
2544
3830
  // only valid values are booleans or numbers between 0 and 1.)
@@ -2565,7 +3851,7 @@ function maybeProfileTransaction(client, transaction, customSamplingContext) {
2565
3851
  }
2566
3852
  const profile_id = uuid4();
2567
3853
  CpuProfilerBindings.startProfiling(profile_id);
2568
- DEBUG_BUILD && logger.log(`[Profiling] started profiling transaction: ${spanToJSON(transaction).description}`);
3854
+ DEBUG_BUILD && logger.log(`[Profiling] started profiling transaction: ${spanToJSON(span).description}`);
2569
3855
  // set transaction context - do this regardless if profiling fails down the line
2570
3856
  // so that we can still see the profile_id in the transaction context
2571
3857
  return profile_id;
@@ -2576,113 +3862,23 @@ function maybeProfileTransaction(client, transaction, customSamplingContext) {
2576
3862
  * @param profile_id
2577
3863
  * @returns
2578
3864
  */
2579
- function stopTransactionProfile(transaction, profile_id) {
3865
+ function stopSpanProfile(span, profile_id) {
2580
3866
  // Should not happen, but satisfy the type checker and be safe regardless.
2581
3867
  if (!profile_id) {
2582
3868
  return null;
2583
3869
  }
2584
3870
  const profile = CpuProfilerBindings.stopProfiling(profile_id);
2585
- DEBUG_BUILD && logger.log(`[Profiling] stopped profiling of transaction: ${spanToJSON(transaction).description}`);
2586
- // In case of an overlapping transaction, stopProfiling may return null and silently ignore the overlapping profile.
3871
+ DEBUG_BUILD && logger.log(`[Profiling] stopped profiling of transaction: ${spanToJSON(span).description}`);
3872
+ // In case of an overlapping span, stopProfiling may return null and silently ignore the overlapping profile.
2587
3873
  if (!profile) {
2588
3874
  DEBUG_BUILD &&
2589
- logger.log(`[Profiling] profiler returned null profile for: ${spanToJSON(transaction).description}`, 'this may indicate an overlapping transaction or a call to stopProfiling with a profile title that was never started');
3875
+ logger.log(`[Profiling] profiler returned null profile for: ${spanToJSON(span).description}`, 'this may indicate an overlapping span or a call to stopProfiling with a profile title that was never started');
2590
3876
  return null;
2591
3877
  }
2592
3878
  // Assign profile_id to the profile
2593
3879
  profile.profile_id = profile_id;
2594
3880
  return profile;
2595
3881
  }
2596
- /**
2597
- * Wraps startTransaction and stopTransaction with profiling related logic.
2598
- * startProfiling is called after the call to startTransaction in order to avoid our own code from
2599
- * being profiled. Because of that same reason, stopProfiling is called before the call to stopTransaction.
2600
- */
2601
- function __PRIVATE__wrapStartTransactionWithProfiling(startTransaction) {
2602
- return function wrappedStartTransaction(transactionContext, customSamplingContext) {
2603
- const transaction = startTransaction.call(this, transactionContext, customSamplingContext);
2604
- // Client is required if we want to profile
2605
- // eslint-disable-next-line deprecation/deprecation
2606
- const client = this.getClient();
2607
- if (!client) {
2608
- return transaction;
2609
- }
2610
- // Check if we should profile this transaction. If a profile_id is returned, then profiling has been started.
2611
- const profile_id = maybeProfileTransaction(client, transaction, customSamplingContext);
2612
- if (!profile_id) {
2613
- return transaction;
2614
- }
2615
- // A couple of important things to note here:
2616
- // `CpuProfilerBindings.stopProfiling` will be scheduled to run in 30seconds in order to exceed max profile duration.
2617
- // Whichever of the two (transaction.finish/timeout) is first to run, the profiling will be stopped and the gathered profile
2618
- // will be processed when the original transaction is finished. Since onProfileHandler can be invoked multiple times in the
2619
- // event of an error or user mistake (calling transaction.finish multiple times), it is important that the behavior of onProfileHandler
2620
- // is idempotent as we do not want any timings or profiles to be overriden by the last call to onProfileHandler.
2621
- // After the original finish method is called, the event will be reported through the integration and delegated to transport.
2622
- let profile = null;
2623
- const options = client.getOptions();
2624
- // Not intended for external use, hence missing types, but we want to profile a couple of things at Sentry that
2625
- // currently exceed the default timeout set by the SDKs.
2626
- const maxProfileDurationMs = (options._experiments && options._experiments['maxProfileDurationMs']) || MAX_PROFILE_DURATION_MS;
2627
- // Enqueue a timeout to prevent profiles from running over max duration.
2628
- let maxDurationTimeoutID = global.setTimeout(() => {
2629
- DEBUG_BUILD &&
2630
- logger.log('[Profiling] max profile duration elapsed, stopping profiling for:', spanToJSON(transaction).description);
2631
- profile = stopTransactionProfile(transaction, profile_id);
2632
- }, maxProfileDurationMs);
2633
- // We need to reference the original finish call to avoid creating an infinite loop
2634
- const originalEnd = transaction.end.bind(transaction);
2635
- // Wrap the transaction finish method to stop profiling and set the profile on the transaction.
2636
- function profilingWrappedTransactionEnd() {
2637
- if (!profile_id) {
2638
- return originalEnd();
2639
- }
2640
- // We stop the handler first to ensure that the timeout is cleared and the profile is stopped.
2641
- if (maxDurationTimeoutID) {
2642
- global.clearTimeout(maxDurationTimeoutID);
2643
- maxDurationTimeoutID = undefined;
2644
- }
2645
- // onProfileHandler should always return the same profile even if this is called multiple times.
2646
- // Always call onProfileHandler to ensure stopProfiling is called and the timeout is cleared.
2647
- if (!profile) {
2648
- profile = stopTransactionProfile(transaction, profile_id);
2649
- }
2650
- // @ts-expect-error profile is not part of metadata
2651
- // eslint-disable-next-line deprecation/deprecation
2652
- transaction.setMetadata({ profile });
2653
- return originalEnd();
2654
- }
2655
- transaction.end = profilingWrappedTransactionEnd;
2656
- return transaction;
2657
- };
2658
- }
2659
- /**
2660
- * Patches startTransaction and stopTransaction with profiling logic.
2661
- * This is used by the SDK's that do not support event hooks.
2662
- * @private
2663
- */
2664
- function _addProfilingExtensionMethods() {
2665
- const carrier = getMainCarrier();
2666
- if (!carrier.__SENTRY__) {
2667
- DEBUG_BUILD && logger.log("[Profiling] Can't find main carrier, profiling won't work.");
2668
- return;
2669
- }
2670
- carrier.__SENTRY__.extensions = carrier.__SENTRY__.extensions || {};
2671
- if (!carrier.__SENTRY__.extensions['startTransaction']) {
2672
- DEBUG_BUILD && logger.log('[Profiling] startTransaction does not exists, profiling will not work.');
2673
- return;
2674
- }
2675
- DEBUG_BUILD && logger.log('[Profiling] startTransaction exists, patching it with profiling functionality...');
2676
- carrier.__SENTRY__.extensions['startTransaction'] = __PRIVATE__wrapStartTransactionWithProfiling(
2677
- // This is patched by sentry/tracing, we are going to re-patch it...
2678
- carrier.__SENTRY__.extensions['startTransaction']);
2679
- }
2680
- /**
2681
- * This patches the global object and injects the Profiling extensions methods
2682
- */
2683
- function addProfilingExtensionMethods() {
2684
- _addProfilingExtensionMethods();
2685
- }
2686
3882
 
2687
3883
  const MAX_PROFILE_QUEUE_LENGTH = 50;
2688
3884
  const PROFILE_QUEUE = [];
@@ -2694,28 +3890,17 @@ function addToProfileQueue(profile) {
2694
3890
  PROFILE_QUEUE.shift();
2695
3891
  }
2696
3892
  }
2697
- /**
2698
- * We need this integration in order to send data to Sentry. We hook into the event processor
2699
- * and inspect each event to see if it is a transaction event and if that transaction event
2700
- * contains a profile on it's metadata. If that is the case, we create a profiling event envelope
2701
- * and delete the profile from the transaction metadata.
2702
- *
2703
- * @deprecated Use `nodeProfilingIntegration` instead.
2704
- */
2705
- class ProfilingIntegration {
2706
- constructor() {
2707
- this.name = 'ProfilingIntegration';
2708
- }
2709
- /**
2710
- * @inheritDoc
2711
- */
2712
- setupOnce(addGlobalEventProcessor, getCurrentHub) {
2713
- this.getCurrentHub = getCurrentHub;
2714
- // eslint-disable-next-line deprecation/deprecation
2715
- const client = this.getCurrentHub().getClient();
2716
- if (client && typeof client.on === 'function') {
2717
- client.on('startTransaction', (transaction) => {
2718
- const profile_id = maybeProfileTransaction(client, transaction, undefined);
3893
+ /** Exported only for tests. */
3894
+ const _nodeProfilingIntegration = (() => {
3895
+ return {
3896
+ name: 'ProfilingIntegration',
3897
+ setup(client) {
3898
+ const spanToProfileIdMap = new WeakMap();
3899
+ client.on('spanStart', span => {
3900
+ if (span !== getRootSpan(span)) {
3901
+ return;
3902
+ }
3903
+ const profile_id = maybeProfileSpan(client, span, undefined);
2719
3904
  if (profile_id) {
2720
3905
  const options = client.getOptions();
2721
3906
  // Not intended for external use, hence missing types, but we want to profile a couple of things at Sentry that
@@ -2729,30 +3914,25 @@ class ProfilingIntegration {
2729
3914
  // Enqueue a timeout to prevent profiles from running over max duration.
2730
3915
  PROFILE_TIMEOUTS[profile_id] = global.setTimeout(() => {
2731
3916
  DEBUG_BUILD &&
2732
- logger.log('[Profiling] max profile duration elapsed, stopping profiling for:', spanToJSON(transaction).description);
2733
- const profile = stopTransactionProfile(transaction, profile_id);
3917
+ logger.log('[Profiling] max profile duration elapsed, stopping profiling for:', spanToJSON(span).description);
3918
+ const profile = stopSpanProfile(span, profile_id);
2734
3919
  if (profile) {
2735
3920
  addToProfileQueue(profile);
2736
3921
  }
2737
3922
  }, maxProfileDurationMs);
2738
- // eslint-disable-next-line deprecation/deprecation
2739
- transaction.setContext('profile', { profile_id });
2740
- // @ts-expect-error profile_id is not part of the metadata type
2741
- // eslint-disable-next-line deprecation/deprecation
2742
- transaction.setMetadata({ profile_id: profile_id });
3923
+ getCurrentScope().setContext('profile', { profile_id });
3924
+ spanToProfileIdMap.set(span, profile_id);
2743
3925
  }
2744
3926
  });
2745
- client.on('finishTransaction', transaction => {
2746
- // @ts-expect-error profile_id is not part of the metadata type
2747
- // eslint-disable-next-line deprecation/deprecation
2748
- const profile_id = transaction.metadata.profile_id;
2749
- if (profile_id && typeof profile_id === 'string') {
3927
+ client.on('spanEnd', span => {
3928
+ const profile_id = spanToProfileIdMap.get(span);
3929
+ if (profile_id) {
2750
3930
  if (PROFILE_TIMEOUTS[profile_id]) {
2751
3931
  global.clearTimeout(PROFILE_TIMEOUTS[profile_id]);
2752
3932
  // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
2753
3933
  delete PROFILE_TIMEOUTS[profile_id];
2754
3934
  }
2755
- const profile = stopTransactionProfile(transaction, profile_id);
3935
+ const profile = stopSpanProfile(span, profile_id);
2756
3936
  if (profile) {
2757
3937
  addToProfileQueue(profile);
2758
3938
  }
@@ -2809,70 +3989,16 @@ class ProfilingIntegration {
2809
3989
  }
2810
3990
  addProfilesToEnvelope(envelope, profilesToAddToEnvelope);
2811
3991
  });
2812
- }
2813
- else {
2814
- // Patch the carrier methods and add the event processor.
2815
- addProfilingExtensionMethods();
2816
- addGlobalEventProcessor(this.handleGlobalEvent.bind(this));
2817
- }
2818
- }
2819
- /**
2820
- * @inheritDoc
2821
- */
2822
- handleGlobalEvent(event) {
2823
- return __awaiter(this, void 0, void 0, function* () {
2824
- if (this.getCurrentHub === undefined) {
2825
- return maybeRemoveProfileFromSdkMetadata(event);
2826
- }
2827
- if (isProfiledTransactionEvent(event)) {
2828
- // Client, Dsn and Transport are all required to be able to send the profiling event to Sentry.
2829
- // If either of them is not available, we remove the profile from the transaction event.
2830
- // and forward it to the next event processor.
2831
- const hub = this.getCurrentHub();
2832
- // eslint-disable-next-line deprecation/deprecation
2833
- const client = hub.getClient();
2834
- if (!client) {
2835
- DEBUG_BUILD &&
2836
- logger.log('[Profiling] getClient did not return a Client, removing profile from event and forwarding to next event processors.');
2837
- return maybeRemoveProfileFromSdkMetadata(event);
2838
- }
2839
- const dsn = client.getDsn();
2840
- if (!dsn) {
2841
- DEBUG_BUILD &&
2842
- logger.log('[Profiling] getDsn did not return a Dsn, removing profile from event and forwarding to next event processors.');
2843
- return maybeRemoveProfileFromSdkMetadata(event);
2844
- }
2845
- const transport = client.getTransport();
2846
- if (!transport) {
2847
- DEBUG_BUILD &&
2848
- logger.log('[Profiling] getTransport did not return a Transport, removing profile from event and forwarding to next event processors.');
2849
- return maybeRemoveProfileFromSdkMetadata(event);
2850
- }
2851
- // If all required components are available, we construct a profiling event envelope and send it to Sentry.
2852
- DEBUG_BUILD && logger.log('[Profiling] Preparing envelope and sending a profiling event');
2853
- const envelope = createProfilingEventEnvelope(event, dsn);
2854
- if (envelope) {
2855
- // Fire and forget, we don't want to block the main event processing flow.
2856
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
2857
- transport.send(envelope);
2858
- }
2859
- }
2860
- // Ensure sdkProcessingMetadata["profile"] is removed from the event before forwarding it to the next event processor.
2861
- return maybeRemoveProfileFromSdkMetadata(event);
2862
- });
2863
- }
2864
- }
3992
+ },
3993
+ };
3994
+ });
2865
3995
  /**
2866
3996
  * We need this integration in order to send data to Sentry. We hook into the event processor
2867
3997
  * and inspect each event to see if it is a transaction event and if that transaction event
2868
3998
  * contains a profile on it's metadata. If that is the case, we create a profiling event envelope
2869
3999
  * and delete the profile from the transaction metadata.
2870
4000
  */
2871
- const nodeProfilingIntegration = (() => {
2872
- // eslint-disable-next-line deprecation/deprecation
2873
- return new ProfilingIntegration();
2874
- });
4001
+ const nodeProfilingIntegration = defineIntegration(_nodeProfilingIntegration);
2875
4002
 
2876
- exports.ProfilingIntegration = ProfilingIntegration;
2877
4003
  exports.nodeProfilingIntegration = nodeProfilingIntegration;
2878
4004
  //# sourceMappingURL=index.js.map