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

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 DELETED
@@ -1,4004 +0,0 @@
1
- Object.defineProperty(exports, '__esModule', { value: true });
2
-
3
- const os = require('os');
4
- const path = require('path');
5
- const detectLibc = require('detect-libc');
6
- const nodeAbi = require('node-abi');
7
- const process$1 = require('process');
8
- const worker_threads = require('worker_threads');
9
-
10
- // eslint-disable-next-line @typescript-eslint/unbound-method
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
- }
30
- /**
31
- * Checks whether given value is an instance of the given built-in class.
32
- *
33
- * @param wat The value to be checked
34
- * @param className
35
- * @returns A boolean representing the result.
36
- */
37
- function isBuiltin(wat, className) {
38
- return objectToString.call(wat) === `[object ${className}]`;
39
- }
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
-
52
- /**
53
- * Checks whether given value's type is an object literal, or a class instance.
54
- * {@link isPlainObject}.
55
- *
56
- * @param wat A value to be checked.
57
- * @returns A boolean representing the result.
58
- */
59
- function isPlainObject(wat) {
60
- return isBuiltin(wat, 'Object');
61
- }
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
-
85
- /**
86
- * Checks whether given value has a then function.
87
- * @param wat A value to be checked.
88
- */
89
- function isThenable(wat) {
90
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
91
- return Boolean(wat && wat.then && typeof wat.then === 'function');
92
- }
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
-
143
- /** Internal global with common properties and Sentry extensions */
144
-
145
- // The code below for 'isGlobalObj' and 'GLOBAL_OBJ' was copied from core-js before modification
146
- // https://github.com/zloirock/core-js/blob/1b944df55282cdc99c90db5f49eb0b6eda2cc0a3/packages/core-js/internals/global.js
147
- // core-js has the following licence:
148
- //
149
- // Copyright (c) 2014-2022 Denis Pushkarev
150
- //
151
- // Permission is hereby granted, free of charge, to any person obtaining a copy
152
- // of this software and associated documentation files (the "Software"), to deal
153
- // in the Software without restriction, including without limitation the rights
154
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
155
- // copies of the Software, and to permit persons to whom the Software is
156
- // furnished to do so, subject to the following conditions:
157
- //
158
- // The above copyright notice and this permission notice shall be included in
159
- // all copies or substantial portions of the Software.
160
- //
161
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
162
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
163
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
164
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
165
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
166
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
167
- // THE SOFTWARE.
168
-
169
- /** Returns 'obj' if it's the global object, otherwise returns undefined */
170
- function isGlobalObj(obj) {
171
- return obj && obj.Math == Math ? obj : undefined;
172
- }
173
-
174
- /** Get's the global object for the current JavaScript runtime */
175
- const GLOBAL_OBJ =
176
- (typeof globalThis == 'object' && isGlobalObj(globalThis)) ||
177
- // eslint-disable-next-line no-restricted-globals
178
- (typeof window == 'object' && isGlobalObj(window)) ||
179
- (typeof self == 'object' && isGlobalObj(self)) ||
180
- (typeof global == 'object' && isGlobalObj(global)) ||
181
- (function () {
182
- return this;
183
- })() ||
184
- {};
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
-
333
- /**
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.
335
- *
336
- * ATTENTION: This constant must never cross package boundaries (i.e. be exported) to guarantee that it can be used for tree shaking.
337
- */
338
- const DEBUG_BUILD$2 = (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__);
339
-
340
- /** Prefix for logging strings */
341
- const PREFIX = 'Sentry Logger ';
342
-
343
- const CONSOLE_LEVELS = [
344
- 'debug',
345
- 'info',
346
- 'warn',
347
- 'error',
348
- 'log',
349
- 'assert',
350
- 'trace',
351
- ] ;
352
-
353
- /** This may be mutated by the console instrumentation. */
354
- const originalConsoleMethods
355
-
356
- = {};
357
-
358
- /** JSDoc */
359
-
360
- /**
361
- * Temporarily disable sentry console instrumentations.
362
- *
363
- * @param callback The function to run against the original `console` messages
364
- * @returns The results of the callback
365
- */
366
- function consoleSandbox(callback) {
367
- if (!('console' in GLOBAL_OBJ)) {
368
- return callback();
369
- }
370
-
371
- const console = GLOBAL_OBJ.console ;
372
- const wrappedFuncs = {};
373
-
374
- const wrappedLevels = Object.keys(originalConsoleMethods) ;
375
-
376
- // Restore all wrapped console methods
377
- wrappedLevels.forEach(level => {
378
- const originalConsoleMethod = originalConsoleMethods[level] ;
379
- wrappedFuncs[level] = console[level] ;
380
- console[level] = originalConsoleMethod;
381
- });
382
-
383
- try {
384
- return callback();
385
- } finally {
386
- // Revert restoration to wrapped state
387
- wrappedLevels.forEach(level => {
388
- console[level] = wrappedFuncs[level] ;
389
- });
390
- }
391
- }
392
-
393
- function makeLogger() {
394
- let enabled = false;
395
- const logger = {
396
- enable: () => {
397
- enabled = true;
398
- },
399
- disable: () => {
400
- enabled = false;
401
- },
402
- isEnabled: () => enabled,
403
- };
404
-
405
- if (DEBUG_BUILD$2) {
406
- CONSOLE_LEVELS.forEach(name => {
407
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
408
- logger[name] = (...args) => {
409
- if (enabled) {
410
- consoleSandbox(() => {
411
- GLOBAL_OBJ.console[name](`${PREFIX}[${name}]:`, ...args);
412
- });
413
- }
414
- };
415
- });
416
- } else {
417
- CONSOLE_LEVELS.forEach(name => {
418
- logger[name] = () => undefined;
419
- });
420
- }
421
-
422
- return logger ;
423
- }
424
-
425
- const logger = makeLogger();
426
-
427
- const defaultFunctionName = '<anonymous>';
428
-
429
- /**
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.
448
- *
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.
459
- *
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.
463
- */
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
- }
518
- }
519
-
520
- /**
521
- * Given any object, return a new object having removed all fields whose value was `undefined`.
522
- * Works recursively on objects and arrays.
523
- *
524
- * Attention: This function keeps circular references in the returned object.
525
- */
526
- function dropUndefinedKeys(inputValue) {
527
- // This map keeps track of what already visited nodes map to.
528
- // Our Set - based memoBuilder doesn't work here because we want to the output object to have the same circular
529
- // references as the input object.
530
- const memoizationMap = new Map();
531
-
532
- // This function just proxies `_dropUndefinedKeys` to keep the `memoBuilder` out of this function's API
533
- return _dropUndefinedKeys(inputValue, memoizationMap);
534
- }
535
-
536
- function _dropUndefinedKeys(inputValue, memoizationMap) {
537
- if (isPojo(inputValue)) {
538
- // If this node has already been visited due to a circular reference, return the object it was mapped to in the new object
539
- const memoVal = memoizationMap.get(inputValue);
540
- if (memoVal !== undefined) {
541
- return memoVal ;
542
- }
543
-
544
- const returnValue = {};
545
- // Store the mapping of this value in case we visit it again, in case of circular data
546
- memoizationMap.set(inputValue, returnValue);
547
-
548
- for (const key of Object.keys(inputValue)) {
549
- if (typeof inputValue[key] !== 'undefined') {
550
- returnValue[key] = _dropUndefinedKeys(inputValue[key], memoizationMap);
551
- }
552
- }
553
-
554
- return returnValue ;
555
- }
556
-
557
- if (Array.isArray(inputValue)) {
558
- // If this node has already been visited due to a circular reference, return the array it was mapped to in the new object
559
- const memoVal = memoizationMap.get(inputValue);
560
- if (memoVal !== undefined) {
561
- return memoVal ;
562
- }
563
-
564
- const returnValue = [];
565
- // Store the mapping of this value in case we visit it again, in case of circular data
566
- memoizationMap.set(inputValue, returnValue);
567
-
568
- inputValue.forEach((item) => {
569
- returnValue.push(_dropUndefinedKeys(item, memoizationMap));
570
- });
571
-
572
- return returnValue ;
573
- }
574
-
575
- return inputValue;
576
- }
577
-
578
- function isPojo(input) {
579
- if (!isPlainObject(input)) {
580
- return false;
581
- }
582
-
583
- try {
584
- const name = (Object.getPrototypeOf(input) ).constructor.name;
585
- return !name || name === 'Object';
586
- } catch (e) {
587
- return true;
588
- }
589
- }
590
-
591
- /**
592
- * UUID4 generator
593
- *
594
- * @returns string Generated UUID4.
595
- */
596
- function uuid4() {
597
- const gbl = GLOBAL_OBJ ;
598
- const crypto = gbl.crypto || gbl.msCrypto;
599
-
600
- let getRandomByte = () => Math.random() * 16;
601
- try {
602
- if (crypto && crypto.randomUUID) {
603
- return crypto.randomUUID().replace(/-/g, '');
604
- }
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,
1257
- };
1258
- }
1259
- }
1260
-
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
- }
1292
- }
1293
-
1294
- const ONE_SECOND_IN_MS = 1000;
1295
-
1296
- /**
1297
- * A partial definition of the [Performance Web API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Performance}
1298
- * for accessing a high-resolution monotonic clock.
1299
- */
1300
-
1301
- /**
1302
- * Returns a timestamp in seconds since the UNIX epoch using the Date API.
1303
- *
1304
- * TODO(v8): Return type should be rounded.
1305
- */
1306
- function dateTimestampInSeconds() {
1307
- return Date.now() / ONE_SECOND_IN_MS;
1308
- }
1309
-
1310
- /**
1311
- * Returns a wrapper around the native Performance API browser implementation, or undefined for browsers that do not
1312
- * support the API.
1313
- *
1314
- * Wrapping the native API works around differences in behavior from different browsers.
1315
- */
1316
- function createUnixTimestampInSecondsFunc() {
1317
- const { performance } = GLOBAL_OBJ ;
1318
- if (!performance || !performance.now) {
1319
- return dateTimestampInSeconds;
1320
- }
1321
-
1322
- // Some browser and environments don't have a timeOrigin, so we fallback to
1323
- // using Date.now() to compute the starting time.
1324
- const approxStartingTimeOrigin = Date.now() - performance.now();
1325
- const timeOrigin = performance.timeOrigin == undefined ? approxStartingTimeOrigin : performance.timeOrigin;
1326
-
1327
- // performance.now() is a monotonic clock, which means it starts at 0 when the process begins. To get the current
1328
- // wall clock time (actual UNIX timestamp), we need to add the starting time origin and the current time elapsed.
1329
- //
1330
- // TODO: This does not account for the case where the monotonic clock that powers performance.now() drifts from the
1331
- // wall clock time, which causes the returned timestamp to be inaccurate. We should investigate how to detect and
1332
- // correct for this.
1333
- // See: https://github.com/getsentry/sentry-javascript/issues/2590
1334
- // See: https://github.com/mdn/content/issues/4713
1335
- // See: https://dev.to/noamr/when-a-millisecond-is-not-a-millisecond-3h6
1336
- return () => {
1337
- return (timeOrigin + performance.now()) / ONE_SECOND_IN_MS;
1338
- };
1339
- }
1340
-
1341
- /**
1342
- * Returns a timestamp in seconds since the UNIX epoch using either the Performance or Date APIs, depending on the
1343
- * availability of the Performance API.
1344
- *
1345
- * BUG: Note that because of how browsers implement the Performance API, the clock might stop when the computer is
1346
- * asleep. This creates a skew between `dateTimestampInSeconds` and `timestampInSeconds`. The
1347
- * skew can grow to arbitrary amounts like days, weeks or months.
1348
- * See https://github.com/getsentry/sentry-javascript/issues/2590.
1349
- */
1350
- const timestampInSeconds = createUnixTimestampInSecondsFunc();
1351
-
1352
- /**
1353
- * The number of milliseconds since the UNIX epoch. This value is only usable in a browser, and only when the
1354
- * performance API is available.
1355
- */
1356
- (() => {
1357
- // Unfortunately browsers may report an inaccurate time origin data, through either performance.timeOrigin or
1358
- // performance.timing.navigationStart, which results in poor results in performance data. We only treat time origin
1359
- // data as reliable if they are within a reasonable threshold of the current time.
1360
-
1361
- const { performance } = GLOBAL_OBJ ;
1362
- if (!performance || !performance.now) {
1363
- return undefined;
1364
- }
1365
-
1366
- const threshold = 3600 * 1000;
1367
- const performanceNow = performance.now();
1368
- const dateNow = Date.now();
1369
-
1370
- // if timeOrigin isn't available set delta to threshold so it isn't used
1371
- const timeOriginDelta = performance.timeOrigin
1372
- ? Math.abs(performance.timeOrigin + performanceNow - dateNow)
1373
- : threshold;
1374
- const timeOriginIsReliable = timeOriginDelta < threshold;
1375
-
1376
- // While performance.timing.navigationStart is deprecated in favor of performance.timeOrigin, performance.timeOrigin
1377
- // is not as widely supported. Namely, performance.timeOrigin is undefined in Safari as of writing.
1378
- // Also as of writing, performance.timing is not available in Web Workers in mainstream browsers, so it is not always
1379
- // a valid fallback. In the absence of an initial time provided by the browser, fallback to the current time from the
1380
- // Date API.
1381
- // eslint-disable-next-line deprecation/deprecation
1382
- const navigationStart = performance.timing && performance.timing.navigationStart;
1383
- const hasNavigationStart = typeof navigationStart === 'number';
1384
- // if navigationStart isn't available set delta to threshold so it isn't used
1385
- const navigationStartDelta = hasNavigationStart ? Math.abs(navigationStart + performanceNow - dateNow) : threshold;
1386
- const navigationStartIsReliable = navigationStartDelta < threshold;
1387
-
1388
- if (timeOriginIsReliable || navigationStartIsReliable) {
1389
- // Use the more reliable time origin
1390
- if (timeOriginDelta <= navigationStartDelta) {
1391
- return performance.timeOrigin;
1392
- } else {
1393
- return navigationStart;
1394
- }
1395
- }
1396
- return dateNow;
1397
- })();
1398
-
1399
- /**
1400
- * Convenience function to loop through the items and item types of an envelope.
1401
- * (This function was mostly created because working with envelope types is painful at the moment)
1402
- *
1403
- * If the callback returns true, the rest of the items will be skipped.
1404
- */
1405
- function forEachEnvelopeItem(
1406
- envelope,
1407
- callback,
1408
- ) {
1409
- const envelopeItems = envelope[1];
1410
-
1411
- for (const envelopeItem of envelopeItems) {
1412
- const envelopeItemType = envelopeItem[0].type;
1413
- const result = callback(envelopeItem, envelopeItemType);
1414
-
1415
- if (result) {
1416
- return true;
1417
- }
1418
- }
1419
-
1420
- return false;
1421
- }
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
-
1430
- /**
1431
- * @private Private API with no semver guarantees!
1432
- *
1433
- * Strategy used to track async context.
1434
- */
1435
-
1436
- /**
1437
- * Returns the global shim registry.
1438
- *
1439
- * FIXME: This function is problematic, because despite always returning a valid Carrier,
1440
- * it has an optional `__SENTRY__` property, which then in turn requires us to always perform an unnecessary check
1441
- * at the call-site. We always access the carrier through this function, so we can guarantee that `__SENTRY__` is there.
1442
- **/
1443
- function getMainCarrier() {
1444
- // This ensures a Sentry carrier exists
1445
- getSentryCarrier(GLOBAL_OBJ);
1446
- return GLOBAL_OBJ;
1447
- }
1448
-
1449
- /** Will either get the existing sentry carrier, or create a new one. */
1450
- function getSentryCarrier(carrier) {
1451
- if (!carrier.__SENTRY__) {
1452
- carrier.__SENTRY__ = {
1453
- extensions: {},
1454
- };
1455
- }
1456
- return carrier.__SENTRY__;
1457
- }
1458
-
1459
- const DEFAULT_ENVIRONMENT = 'production';
1460
-
1461
- /**
1462
- * Creates a new `Session` object by setting certain default parameters. If optional @param context
1463
- * is passed, the passed properties are applied to the session object.
1464
- *
1465
- * @param context (optional) additional properties to be applied to the returned session object
1466
- *
1467
- * @returns a new `Session` object
1468
- */
1469
- function makeSession(context) {
1470
- // Both timestamp and started are in seconds since the UNIX epoch.
1471
- const startingTime = timestampInSeconds();
1472
-
1473
- const session = {
1474
- sid: uuid4(),
1475
- init: true,
1476
- timestamp: startingTime,
1477
- started: startingTime,
1478
- duration: 0,
1479
- status: 'ok',
1480
- errors: 0,
1481
- ignoreDuration: false,
1482
- toJSON: () => sessionToJSON(session),
1483
- };
1484
-
1485
- if (context) {
1486
- updateSession(session, context);
1487
- }
1488
-
1489
- return session;
1490
- }
1491
-
1492
- /**
1493
- * Updates a session object with the properties passed in the context.
1494
- *
1495
- * Note that this function mutates the passed object and returns void.
1496
- * (Had to do this instead of returning a new and updated session because closing and sending a session
1497
- * makes an update to the session after it was passed to the sending logic.
1498
- * @see BaseClient.captureSession )
1499
- *
1500
- * @param session the `Session` to update
1501
- * @param context the `SessionContext` holding the properties that should be updated in @param session
1502
- */
1503
- // eslint-disable-next-line complexity
1504
- function updateSession(session, context = {}) {
1505
- if (context.user) {
1506
- if (!session.ipAddress && context.user.ip_address) {
1507
- session.ipAddress = context.user.ip_address;
1508
- }
1509
-
1510
- if (!session.did && !context.did) {
1511
- session.did = context.user.id || context.user.email || context.user.username;
1512
- }
1513
- }
1514
-
1515
- session.timestamp = context.timestamp || timestampInSeconds();
1516
-
1517
- if (context.abnormal_mechanism) {
1518
- session.abnormal_mechanism = context.abnormal_mechanism;
1519
- }
1520
-
1521
- if (context.ignoreDuration) {
1522
- session.ignoreDuration = context.ignoreDuration;
1523
- }
1524
- if (context.sid) {
1525
- // Good enough uuid validation. — Kamil
1526
- session.sid = context.sid.length === 32 ? context.sid : uuid4();
1527
- }
1528
- if (context.init !== undefined) {
1529
- session.init = context.init;
1530
- }
1531
- if (!session.did && context.did) {
1532
- session.did = `${context.did}`;
1533
- }
1534
- if (typeof context.started === 'number') {
1535
- session.started = context.started;
1536
- }
1537
- if (session.ignoreDuration) {
1538
- session.duration = undefined;
1539
- } else if (typeof context.duration === 'number') {
1540
- session.duration = context.duration;
1541
- } else {
1542
- const duration = session.timestamp - session.started;
1543
- session.duration = duration >= 0 ? duration : 0;
1544
- }
1545
- if (context.release) {
1546
- session.release = context.release;
1547
- }
1548
- if (context.environment) {
1549
- session.environment = context.environment;
1550
- }
1551
- if (!session.ipAddress && context.ipAddress) {
1552
- session.ipAddress = context.ipAddress;
1553
- }
1554
- if (!session.userAgent && context.userAgent) {
1555
- session.userAgent = context.userAgent;
1556
- }
1557
- if (typeof context.errors === 'number') {
1558
- session.errors = context.errors;
1559
- }
1560
- if (context.status) {
1561
- session.status = context.status;
1562
- }
1563
- }
1564
-
1565
- /**
1566
- * Closes a session by setting its status and updating the session object with it.
1567
- * Internally calls `updateSession` to update the passed session object.
1568
- *
1569
- * Note that this function mutates the passed session (@see updateSession for explanation).
1570
- *
1571
- * @param session the `Session` object to be closed
1572
- * @param status the `SessionStatus` with which the session was closed. If you don't pass a status,
1573
- * this function will keep the previously set status, unless it was `'ok'` in which case
1574
- * it is changed to `'exited'`.
1575
- */
1576
- function closeSession(session, status) {
1577
- let context = {};
1578
- if (status) {
1579
- context = { status };
1580
- } else if (session.status === 'ok') {
1581
- context = { status: 'exited' };
1582
- }
1583
-
1584
- updateSession(session, context);
1585
- }
1586
-
1587
- /**
1588
- * Serializes a passed session object to a JSON object with a slightly different structure.
1589
- * This is necessary because the Sentry backend requires a slightly different schema of a session
1590
- * than the one the JS SDKs use internally.
1591
- *
1592
- * @param session the session to be converted
1593
- *
1594
- * @returns a JSON object of the passed session
1595
- */
1596
- function sessionToJSON(session) {
1597
- return dropUndefinedKeys({
1598
- sid: `${session.sid}`,
1599
- init: session.init,
1600
- // Make sure that sec is converted to ms for date constructor
1601
- started: new Date(session.started * 1000).toISOString(),
1602
- timestamp: new Date(session.timestamp * 1000).toISOString(),
1603
- status: session.status,
1604
- errors: session.errors,
1605
- did: typeof session.did === 'number' || typeof session.did === 'string' ? `${session.did}` : undefined,
1606
- duration: session.duration,
1607
- abnormal_mechanism: session.abnormal_mechanism,
1608
- attrs: {
1609
- release: session.release,
1610
- environment: session.environment,
1611
- ip_address: session.ipAddress,
1612
- user_agent: session.userAgent,
1613
- },
1614
- });
1615
- }
1616
-
1617
- /**
1618
- * Default value for maximum number of breadcrumbs added to an event.
1619
- */
1620
- const DEFAULT_MAX_BREADCRUMBS = 100;
1621
-
1622
- /**
1623
- * Holds additional event information.
1624
- */
1625
- class Scope {
1626
- /** Flag if notifying is happening. */
1627
-
1628
- /** Callback for client to receive scope changes. */
1629
-
1630
- /** Callback list that will be called during event processing. */
1631
-
1632
- /** Array of breadcrumbs. */
1633
-
1634
- /** User */
1635
-
1636
- /** Tags */
1637
-
1638
- /** Extra */
1639
-
1640
- /** Contexts */
1641
-
1642
- /** Attachments */
1643
-
1644
- /** Propagation Context for distributed tracing */
1645
-
1646
- /**
1647
- * A place to stash data which is needed at some point in the SDK's event processing pipeline but which shouldn't get
1648
- * sent to Sentry
1649
- */
1650
-
1651
- /** Fingerprint */
1652
-
1653
- /** Severity */
1654
-
1655
- /**
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.
1660
- */
1661
-
1662
- /** Span */
1663
-
1664
- /** Session */
1665
-
1666
- /** Request Mode Session Status */
1667
-
1668
- /** The client on this scope */
1669
-
1670
- // NOTE: Any field which gets added here should get added not only to the constructor but also to the `clone` method.
1671
-
1672
- constructor() {
1673
- this._notifyingListeners = false;
1674
- this._scopeListeners = [];
1675
- this._eventProcessors = [];
1676
- this._breadcrumbs = [];
1677
- this._attachments = [];
1678
- this._user = {};
1679
- this._tags = {};
1680
- this._extra = {};
1681
- this._contexts = {};
1682
- this._sdkProcessingMetadata = {};
1683
- this._propagationContext = generatePropagationContext();
1684
- }
1685
-
1686
- /**
1687
- * Inherit values from the parent scope.
1688
- * @deprecated Use `scope.clone()` and `new Scope()` instead.
1689
- */
1690
- static clone(scope) {
1691
- return scope ? scope.clone() : new Scope();
1692
- }
1693
-
1694
- /**
1695
- * @inheritDoc
1696
- */
1697
- clone() {
1698
- const newScope = new Scope();
1699
- newScope._breadcrumbs = [...this._breadcrumbs];
1700
- newScope._tags = { ...this._tags };
1701
- newScope._extra = { ...this._extra };
1702
- newScope._contexts = { ...this._contexts };
1703
- newScope._user = this._user;
1704
- newScope._level = this._level;
1705
- newScope._span = this._span;
1706
- newScope._session = this._session;
1707
- newScope._transactionName = this._transactionName;
1708
- newScope._fingerprint = this._fingerprint;
1709
- newScope._eventProcessors = [...this._eventProcessors];
1710
- newScope._requestSession = this._requestSession;
1711
- newScope._attachments = [...this._attachments];
1712
- newScope._sdkProcessingMetadata = { ...this._sdkProcessingMetadata };
1713
- newScope._propagationContext = { ...this._propagationContext };
1714
- newScope._client = this._client;
1715
-
1716
- return newScope;
1717
- }
1718
-
1719
- /**
1720
- * @inheritDoc
1721
- */
1722
- setClient(client) {
1723
- this._client = client;
1724
- }
1725
-
1726
- /**
1727
- * @inheritDoc
1728
- */
1729
- getClient() {
1730
- return this._client ;
1731
- }
1732
-
1733
- /**
1734
- * @inheritDoc
1735
- */
1736
- addScopeListener(callback) {
1737
- this._scopeListeners.push(callback);
1738
- }
1739
-
1740
- /**
1741
- * @inheritDoc
1742
- */
1743
- addEventProcessor(callback) {
1744
- this._eventProcessors.push(callback);
1745
- return this;
1746
- }
1747
-
1748
- /**
1749
- * @inheritDoc
1750
- */
1751
- setUser(user) {
1752
- // If null is passed we want to unset everything, but still define keys,
1753
- // so that later down in the pipeline any existing values are cleared.
1754
- this._user = user || {
1755
- email: undefined,
1756
- id: undefined,
1757
- ip_address: undefined,
1758
- username: undefined,
1759
- };
1760
-
1761
- if (this._session) {
1762
- updateSession(this._session, { user });
1763
- }
1764
-
1765
- this._notifyScopeListeners();
1766
- return this;
1767
- }
1768
-
1769
- /**
1770
- * @inheritDoc
1771
- */
1772
- getUser() {
1773
- return this._user;
1774
- }
1775
-
1776
- /**
1777
- * @inheritDoc
1778
- */
1779
- getRequestSession() {
1780
- return this._requestSession;
1781
- }
1782
-
1783
- /**
1784
- * @inheritDoc
1785
- */
1786
- setRequestSession(requestSession) {
1787
- this._requestSession = requestSession;
1788
- return this;
1789
- }
1790
-
1791
- /**
1792
- * @inheritDoc
1793
- */
1794
- setTags(tags) {
1795
- this._tags = {
1796
- ...this._tags,
1797
- ...tags,
1798
- };
1799
- this._notifyScopeListeners();
1800
- return this;
1801
- }
1802
-
1803
- /**
1804
- * @inheritDoc
1805
- */
1806
- setTag(key, value) {
1807
- this._tags = { ...this._tags, [key]: value };
1808
- this._notifyScopeListeners();
1809
- return this;
1810
- }
1811
-
1812
- /**
1813
- * @inheritDoc
1814
- */
1815
- setExtras(extras) {
1816
- this._extra = {
1817
- ...this._extra,
1818
- ...extras,
1819
- };
1820
- this._notifyScopeListeners();
1821
- return this;
1822
- }
1823
-
1824
- /**
1825
- * @inheritDoc
1826
- */
1827
- setExtra(key, extra) {
1828
- this._extra = { ...this._extra, [key]: extra };
1829
- this._notifyScopeListeners();
1830
- return this;
1831
- }
1832
-
1833
- /**
1834
- * @inheritDoc
1835
- */
1836
- setFingerprint(fingerprint) {
1837
- this._fingerprint = fingerprint;
1838
- this._notifyScopeListeners();
1839
- return this;
1840
- }
1841
-
1842
- /**
1843
- * @inheritDoc
1844
- */
1845
- setLevel(level) {
1846
- this._level = level;
1847
- this._notifyScopeListeners();
1848
- return this;
1849
- }
1850
-
1851
- /**
1852
- * @inheritDoc
1853
- */
1854
- setTransactionName(name) {
1855
- this._transactionName = name;
1856
- this._notifyScopeListeners();
1857
- return this;
1858
- }
1859
-
1860
- /**
1861
- * @inheritDoc
1862
- */
1863
- setContext(key, context) {
1864
- if (context === null) {
1865
- // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
1866
- delete this._contexts[key];
1867
- } else {
1868
- this._contexts[key] = context;
1869
- }
1870
-
1871
- this._notifyScopeListeners();
1872
- return this;
1873
- }
1874
-
1875
- /**
1876
- * Sets the Span on the scope.
1877
- * @param span Span
1878
- * @deprecated Instead of setting a span on a scope, use `startSpan()`/`startSpanManual()` instead.
1879
- */
1880
- setSpan(span) {
1881
- this._span = span;
1882
- this._notifyScopeListeners();
1883
- return this;
1884
- }
1885
-
1886
- /**
1887
- * Returns the `Span` if there is one.
1888
- * @deprecated Use `getActiveSpan()` instead.
1889
- */
1890
- getSpan() {
1891
- return this._span;
1892
- }
1893
-
1894
- /**
1895
- * Returns the `Transaction` attached to the scope (if there is one).
1896
- * @deprecated You should not rely on the transaction, but just use `startSpan()` APIs instead.
1897
- */
1898
- getTransaction() {
1899
- // Often, this span (if it exists at all) will be a transaction, but it's not guaranteed to be. Regardless, it will
1900
- // have a pointer to the currently-active transaction.
1901
- const span = this._span;
1902
-
1903
- // Cannot replace with getRootSpan because getRootSpan returns a span, not a transaction
1904
- // Also, this method will be removed anyway.
1905
- // eslint-disable-next-line deprecation/deprecation
1906
- if (span && (span ).transaction) {
1907
- // eslint-disable-next-line deprecation/deprecation
1908
- return (span ).transaction;
1909
- }
1910
- return undefined;
1911
- }
1912
-
1913
- /**
1914
- * @inheritDoc
1915
- */
1916
- setSession(session) {
1917
- if (!session) {
1918
- delete this._session;
1919
- } else {
1920
- this._session = session;
1921
- }
1922
- this._notifyScopeListeners();
1923
- return this;
1924
- }
1925
-
1926
- /**
1927
- * @inheritDoc
1928
- */
1929
- getSession() {
1930
- return this._session;
1931
- }
1932
-
1933
- /**
1934
- * @inheritDoc
1935
- */
1936
- update(captureContext) {
1937
- if (!captureContext) {
1938
- return this;
1939
- }
1940
-
1941
- const scopeToMerge = typeof captureContext === 'function' ? captureContext(this) : captureContext;
1942
-
1943
- if (scopeToMerge instanceof Scope) {
1944
- const scopeData = scopeToMerge.getScopeData();
1945
-
1946
- this._tags = { ...this._tags, ...scopeData.tags };
1947
- this._extra = { ...this._extra, ...scopeData.extra };
1948
- this._contexts = { ...this._contexts, ...scopeData.contexts };
1949
- if (scopeData.user && Object.keys(scopeData.user).length) {
1950
- this._user = scopeData.user;
1951
- }
1952
- if (scopeData.level) {
1953
- this._level = scopeData.level;
1954
- }
1955
- if (scopeData.fingerprint.length) {
1956
- this._fingerprint = scopeData.fingerprint;
1957
- }
1958
- if (scopeToMerge.getRequestSession()) {
1959
- this._requestSession = scopeToMerge.getRequestSession();
1960
- }
1961
- if (scopeData.propagationContext) {
1962
- this._propagationContext = scopeData.propagationContext;
1963
- }
1964
- } else if (isPlainObject(scopeToMerge)) {
1965
- const scopeContext = captureContext ;
1966
- this._tags = { ...this._tags, ...scopeContext.tags };
1967
- this._extra = { ...this._extra, ...scopeContext.extra };
1968
- this._contexts = { ...this._contexts, ...scopeContext.contexts };
1969
- if (scopeContext.user) {
1970
- this._user = scopeContext.user;
1971
- }
1972
- if (scopeContext.level) {
1973
- this._level = scopeContext.level;
1974
- }
1975
- if (scopeContext.fingerprint) {
1976
- this._fingerprint = scopeContext.fingerprint;
1977
- }
1978
- if (scopeContext.requestSession) {
1979
- this._requestSession = scopeContext.requestSession;
1980
- }
1981
- if (scopeContext.propagationContext) {
1982
- this._propagationContext = scopeContext.propagationContext;
1983
- }
1984
- }
1985
-
1986
- return this;
1987
- }
1988
-
1989
- /**
1990
- * @inheritDoc
1991
- */
1992
- clear() {
1993
- // client is not cleared here on purpose!
1994
- this._breadcrumbs = [];
1995
- this._tags = {};
1996
- this._extra = {};
1997
- this._user = {};
1998
- this._contexts = {};
1999
- this._level = undefined;
2000
- this._transactionName = undefined;
2001
- this._fingerprint = undefined;
2002
- this._requestSession = undefined;
2003
- this._span = undefined;
2004
- this._session = undefined;
2005
- this._notifyScopeListeners();
2006
- this._attachments = [];
2007
- this._propagationContext = generatePropagationContext();
2008
- return this;
2009
- }
2010
-
2011
- /**
2012
- * @inheritDoc
2013
- */
2014
- addBreadcrumb(breadcrumb, maxBreadcrumbs) {
2015
- const maxCrumbs = typeof maxBreadcrumbs === 'number' ? maxBreadcrumbs : DEFAULT_MAX_BREADCRUMBS;
2016
-
2017
- // No data has been changed, so don't notify scope listeners
2018
- if (maxCrumbs <= 0) {
2019
- return this;
2020
- }
2021
-
2022
- const mergedBreadcrumb = {
2023
- timestamp: dateTimestampInSeconds(),
2024
- ...breadcrumb,
2025
- };
2026
-
2027
- const breadcrumbs = this._breadcrumbs;
2028
- breadcrumbs.push(mergedBreadcrumb);
2029
- this._breadcrumbs = breadcrumbs.length > maxCrumbs ? breadcrumbs.slice(-maxCrumbs) : breadcrumbs;
2030
-
2031
- this._notifyScopeListeners();
2032
-
2033
- return this;
2034
- }
2035
-
2036
- /**
2037
- * @inheritDoc
2038
- */
2039
- getLastBreadcrumb() {
2040
- return this._breadcrumbs[this._breadcrumbs.length - 1];
2041
- }
2042
-
2043
- /**
2044
- * @inheritDoc
2045
- */
2046
- clearBreadcrumbs() {
2047
- this._breadcrumbs = [];
2048
- this._notifyScopeListeners();
2049
- return this;
2050
- }
2051
-
2052
- /**
2053
- * @inheritDoc
2054
- */
2055
- addAttachment(attachment) {
2056
- this._attachments.push(attachment);
2057
- return this;
2058
- }
2059
-
2060
- /**
2061
- * @inheritDoc
2062
- * @deprecated Use `getScopeData()` instead.
2063
- */
2064
- getAttachments() {
2065
- const data = this.getScopeData();
2066
-
2067
- return data.attachments;
2068
- }
2069
-
2070
- /**
2071
- * @inheritDoc
2072
- */
2073
- clearAttachments() {
2074
- this._attachments = [];
2075
- return this;
2076
- }
2077
-
2078
- /** @inheritDoc */
2079
- getScopeData() {
2080
- const {
2081
- _breadcrumbs,
2082
- _attachments,
2083
- _contexts,
2084
- _tags,
2085
- _extra,
2086
- _user,
2087
- _level,
2088
- _fingerprint,
2089
- _eventProcessors,
2090
- _propagationContext,
2091
- _sdkProcessingMetadata,
2092
- _transactionName,
2093
- _span,
2094
- } = this;
2095
-
2096
- return {
2097
- breadcrumbs: _breadcrumbs,
2098
- attachments: _attachments,
2099
- contexts: _contexts,
2100
- tags: _tags,
2101
- extra: _extra,
2102
- user: _user,
2103
- level: _level,
2104
- fingerprint: _fingerprint || [],
2105
- eventProcessors: _eventProcessors,
2106
- propagationContext: _propagationContext,
2107
- sdkProcessingMetadata: _sdkProcessingMetadata,
2108
- transactionName: _transactionName,
2109
- span: _span,
2110
- };
2111
- }
2112
-
2113
- /**
2114
- * @inheritDoc
2115
- */
2116
- setSDKProcessingMetadata(newData) {
2117
- this._sdkProcessingMetadata = { ...this._sdkProcessingMetadata, ...newData };
2118
-
2119
- return this;
2120
- }
2121
-
2122
- /**
2123
- * @inheritDoc
2124
- */
2125
- setPropagationContext(context) {
2126
- this._propagationContext = context;
2127
- return this;
2128
- }
2129
-
2130
- /**
2131
- * @inheritDoc
2132
- */
2133
- getPropagationContext() {
2134
- return this._propagationContext;
2135
- }
2136
-
2137
- /**
2138
- * @inheritDoc
2139
- */
2140
- captureException(exception, hint) {
2141
- const eventId = hint && hint.event_id ? hint.event_id : uuid4();
2142
-
2143
- if (!this._client) {
2144
- logger.warn('No client configured on scope - will not capture exception!');
2145
- return eventId;
2146
- }
2147
-
2148
- const syntheticException = new Error('Sentry syntheticException');
2149
-
2150
- this._client.captureException(
2151
- exception,
2152
- {
2153
- originalException: exception,
2154
- syntheticException,
2155
- ...hint,
2156
- event_id: eventId,
2157
- },
2158
- this,
2159
- );
2160
-
2161
- return eventId;
2162
- }
2163
-
2164
- /**
2165
- * @inheritDoc
2166
- */
2167
- captureMessage(message, level, hint) {
2168
- const eventId = hint && hint.event_id ? hint.event_id : uuid4();
2169
-
2170
- if (!this._client) {
2171
- logger.warn('No client configured on scope - will not capture message!');
2172
- return eventId;
2173
- }
2174
-
2175
- const syntheticException = new Error(message);
2176
-
2177
- this._client.captureMessage(
2178
- message,
2179
- level,
2180
- {
2181
- originalException: message,
2182
- syntheticException,
2183
- ...hint,
2184
- event_id: eventId,
2185
- },
2186
- this,
2187
- );
2188
-
2189
- return eventId;
2190
- }
2191
-
2192
- /**
2193
- * @inheritDoc
2194
- */
2195
- captureEvent(event, hint) {
2196
- const eventId = hint && hint.event_id ? hint.event_id : uuid4();
2197
-
2198
- if (!this._client) {
2199
- logger.warn('No client configured on scope - will not capture event!');
2200
- return eventId;
2201
- }
2202
-
2203
- this._client.captureEvent(event, { ...hint, event_id: eventId }, this);
2204
-
2205
- return eventId;
2206
- }
2207
-
2208
- /**
2209
- * This will be called on every set call.
2210
- */
2211
- _notifyScopeListeners() {
2212
- // We need this check for this._notifyingListeners to be able to work on scope during updates
2213
- // If this check is not here we'll produce endless recursion when something is done with the scope
2214
- // during the callback.
2215
- if (!this._notifyingListeners) {
2216
- this._notifyingListeners = true;
2217
- this._scopeListeners.forEach(callback => {
2218
- callback(this);
2219
- });
2220
- this._notifyingListeners = false;
2221
- }
2222
- }
2223
- }
2224
-
2225
- function generatePropagationContext() {
2226
- return {
2227
- traceId: uuid4(),
2228
- spanId: uuid4().substring(16),
2229
- };
2230
- }
2231
-
2232
- const SDK_VERSION = '8.0.0-alpha.3';
2233
-
2234
- /**
2235
- * API compatibility version of this hub.
2236
- *
2237
- * WARNING: This number should only be increased when the global interface
2238
- * changes and new methods are introduced.
2239
- *
2240
- * @hidden
2241
- */
2242
- const API_VERSION = parseFloat(SDK_VERSION);
2243
-
2244
- /**
2245
- * Default maximum number of breadcrumbs added to an event. Can be overwritten
2246
- * with {@link Options.maxBreadcrumbs}.
2247
- */
2248
- const DEFAULT_BREADCRUMBS$1 = 100;
2249
-
2250
- /**
2251
- * A layer in the process stack.
2252
- * @hidden
2253
- */
2254
-
2255
- /**
2256
- * @inheritDoc
2257
- */
2258
- class Hub {
2259
- /** Is a {@link Layer}[] containing the client and scope */
2260
-
2261
- /**
2262
- * Creates a new instance of the hub, will push one {@link Layer} into the
2263
- * internal stack on creation.
2264
- *
2265
- * @param client bound to the hub.
2266
- * @param scope bound to the hub.
2267
- * @param version number, higher number means higher priority.
2268
- *
2269
- * @deprecated Instantiation of Hub objects is deprecated and the constructor will be removed in version 8 of the SDK.
2270
- *
2271
- * If you are currently using the Hub for multi-client use like so:
2272
- *
2273
- * ```
2274
- * // OLD
2275
- * const hub = new Hub();
2276
- * hub.bindClient(client);
2277
- * makeMain(hub)
2278
- * ```
2279
- *
2280
- * instead initialize the client as follows:
2281
- *
2282
- * ```
2283
- * // NEW
2284
- * Sentry.withIsolationScope(() => {
2285
- * Sentry.setCurrentClient(client);
2286
- * client.init();
2287
- * });
2288
- * ```
2289
- *
2290
- * If you are using the Hub to capture events like so:
2291
- *
2292
- * ```
2293
- * // OLD
2294
- * const client = new Client();
2295
- * const hub = new Hub(client);
2296
- * hub.captureException()
2297
- * ```
2298
- *
2299
- * instead capture isolated events as follows:
2300
- *
2301
- * ```
2302
- * // NEW
2303
- * const client = new Client();
2304
- * const scope = new Scope();
2305
- * scope.setClient(client);
2306
- * scope.captureException();
2307
- * ```
2308
- */
2309
- constructor(
2310
- client,
2311
- scope,
2312
- isolationScope,
2313
- _version = API_VERSION,
2314
- ) {this._version = _version;
2315
- let assignedScope;
2316
- if (!scope) {
2317
- assignedScope = new Scope();
2318
- assignedScope.setClient(client);
2319
- } else {
2320
- assignedScope = scope;
2321
- }
2322
-
2323
- let assignedIsolationScope;
2324
- if (!isolationScope) {
2325
- assignedIsolationScope = new Scope();
2326
- assignedIsolationScope.setClient(client);
2327
- } else {
2328
- assignedIsolationScope = isolationScope;
2329
- }
2330
-
2331
- this._stack = [{ scope: assignedScope }];
2332
-
2333
- if (client) {
2334
- // eslint-disable-next-line deprecation/deprecation
2335
- this.bindClient(client);
2336
- }
2337
-
2338
- this._isolationScope = assignedIsolationScope;
2339
- }
2340
-
2341
- /**
2342
- * Checks if this hub's version is older than the given version.
2343
- *
2344
- * @param version A version number to compare to.
2345
- * @return True if the given version is newer; otherwise false.
2346
- *
2347
- * @deprecated This will be removed in v8.
2348
- */
2349
- isOlderThan(version) {
2350
- return this._version < version;
2351
- }
2352
-
2353
- /**
2354
- * This binds the given client to the current scope.
2355
- * @param client An SDK client (client) instance.
2356
- *
2357
- * @deprecated Use `initAndBind()` directly, or `setCurrentClient()` and/or `client.init()` instead.
2358
- */
2359
- bindClient(client) {
2360
- // eslint-disable-next-line deprecation/deprecation
2361
- const top = this.getStackTop();
2362
- top.client = client;
2363
- top.scope.setClient(client);
2364
- // eslint-disable-next-line deprecation/deprecation
2365
- if (client && client.setupIntegrations) {
2366
- // eslint-disable-next-line deprecation/deprecation
2367
- client.setupIntegrations();
2368
- }
2369
- }
2370
-
2371
- /**
2372
- * @inheritDoc
2373
- *
2374
- * @deprecated Use `withScope` instead.
2375
- */
2376
- pushScope() {
2377
- // We want to clone the content of prev scope
2378
- // eslint-disable-next-line deprecation/deprecation
2379
- const scope = this.getScope().clone();
2380
- // eslint-disable-next-line deprecation/deprecation
2381
- this.getStack().push({
2382
- // eslint-disable-next-line deprecation/deprecation
2383
- client: this.getClient(),
2384
- scope,
2385
- });
2386
- return scope;
2387
- }
2388
-
2389
- /**
2390
- * @inheritDoc
2391
- *
2392
- * @deprecated Use `withScope` instead.
2393
- */
2394
- popScope() {
2395
- // eslint-disable-next-line deprecation/deprecation
2396
- if (this.getStack().length <= 1) return false;
2397
- // eslint-disable-next-line deprecation/deprecation
2398
- return !!this.getStack().pop();
2399
- }
2400
-
2401
- /**
2402
- * @inheritDoc
2403
- *
2404
- * @deprecated Use `Sentry.withScope()` instead.
2405
- */
2406
- withScope(callback) {
2407
- // eslint-disable-next-line deprecation/deprecation
2408
- const scope = this.pushScope();
2409
-
2410
- let maybePromiseResult;
2411
- try {
2412
- maybePromiseResult = callback(scope);
2413
- } catch (e) {
2414
- // eslint-disable-next-line deprecation/deprecation
2415
- this.popScope();
2416
- throw e;
2417
- }
2418
-
2419
- if (isThenable(maybePromiseResult)) {
2420
- // @ts-expect-error - isThenable returns the wrong type
2421
- return maybePromiseResult.then(
2422
- res => {
2423
- // eslint-disable-next-line deprecation/deprecation
2424
- this.popScope();
2425
- return res;
2426
- },
2427
- e => {
2428
- // eslint-disable-next-line deprecation/deprecation
2429
- this.popScope();
2430
- throw e;
2431
- },
2432
- );
2433
- }
2434
-
2435
- // eslint-disable-next-line deprecation/deprecation
2436
- this.popScope();
2437
- return maybePromiseResult;
2438
- }
2439
-
2440
- /**
2441
- * @inheritDoc
2442
- *
2443
- * @deprecated Use `Sentry.getClient()` instead.
2444
- */
2445
- getClient() {
2446
- // eslint-disable-next-line deprecation/deprecation
2447
- return this.getStackTop().client ;
2448
- }
2449
-
2450
- /**
2451
- * Returns the scope of the top stack.
2452
- *
2453
- * @deprecated Use `Sentry.getCurrentScope()` instead.
2454
- */
2455
- getScope() {
2456
- // eslint-disable-next-line deprecation/deprecation
2457
- return this.getStackTop().scope;
2458
- }
2459
-
2460
- /**
2461
- * @deprecated Use `Sentry.getIsolationScope()` instead.
2462
- */
2463
- getIsolationScope() {
2464
- return this._isolationScope;
2465
- }
2466
-
2467
- /**
2468
- * Returns the scope stack for domains or the process.
2469
- * @deprecated This will be removed in v8.
2470
- */
2471
- getStack() {
2472
- return this._stack;
2473
- }
2474
-
2475
- /**
2476
- * Returns the topmost scope layer in the order domain > local > process.
2477
- * @deprecated This will be removed in v8.
2478
- */
2479
- getStackTop() {
2480
- return this._stack[this._stack.length - 1];
2481
- }
2482
-
2483
- /**
2484
- * @inheritDoc
2485
- *
2486
- * @deprecated Use `Sentry.captureException()` instead.
2487
- */
2488
- captureException(exception, hint) {
2489
- const eventId = hint && hint.event_id ? hint.event_id : uuid4();
2490
- const syntheticException = new Error('Sentry syntheticException');
2491
- // eslint-disable-next-line deprecation/deprecation
2492
- this.getScope().captureException(exception, {
2493
- originalException: exception,
2494
- syntheticException,
2495
- ...hint,
2496
- event_id: eventId,
2497
- });
2498
-
2499
- return eventId;
2500
- }
2501
-
2502
- /**
2503
- * @inheritDoc
2504
- *
2505
- * @deprecated Use `Sentry.captureMessage()` instead.
2506
- */
2507
- captureMessage(message, level, hint) {
2508
- const eventId = hint && hint.event_id ? hint.event_id : uuid4();
2509
- const syntheticException = new Error(message);
2510
- // eslint-disable-next-line deprecation/deprecation
2511
- this.getScope().captureMessage(message, level, {
2512
- originalException: message,
2513
- syntheticException,
2514
- ...hint,
2515
- event_id: eventId,
2516
- });
2517
-
2518
- return eventId;
2519
- }
2520
-
2521
- /**
2522
- * @inheritDoc
2523
- *
2524
- * @deprecated Use `Sentry.captureEvent()` instead.
2525
- */
2526
- captureEvent(event, hint) {
2527
- const eventId = hint && hint.event_id ? hint.event_id : uuid4();
2528
- // eslint-disable-next-line deprecation/deprecation
2529
- this.getScope().captureEvent(event, { ...hint, event_id: eventId });
2530
- return eventId;
2531
- }
2532
-
2533
- /**
2534
- * @inheritDoc
2535
- *
2536
- * @deprecated Use `Sentry.addBreadcrumb()` instead.
2537
- */
2538
- addBreadcrumb(breadcrumb, hint) {
2539
- // eslint-disable-next-line deprecation/deprecation
2540
- const { client } = this.getStackTop();
2541
-
2542
- if (!client) return;
2543
-
2544
- const { beforeBreadcrumb = null, maxBreadcrumbs = DEFAULT_BREADCRUMBS$1 } =
2545
- (client.getOptions && client.getOptions()) || {};
2546
-
2547
- if (maxBreadcrumbs <= 0) return;
2548
-
2549
- const timestamp = dateTimestampInSeconds();
2550
- const mergedBreadcrumb = { timestamp, ...breadcrumb };
2551
- const finalBreadcrumb = beforeBreadcrumb
2552
- ? (consoleSandbox(() => beforeBreadcrumb(mergedBreadcrumb, hint)) )
2553
- : mergedBreadcrumb;
2554
-
2555
- if (finalBreadcrumb === null) return;
2556
-
2557
- client.emit('beforeAddBreadcrumb', finalBreadcrumb, hint);
2558
-
2559
- // eslint-disable-next-line deprecation/deprecation
2560
- this.getIsolationScope().addBreadcrumb(finalBreadcrumb, maxBreadcrumbs);
2561
- }
2562
-
2563
- /**
2564
- * @inheritDoc
2565
- * @deprecated Use `Sentry.setUser()` instead.
2566
- */
2567
- setUser(user) {
2568
- // eslint-disable-next-line deprecation/deprecation
2569
- this.getIsolationScope().setUser(user);
2570
- }
2571
-
2572
- /**
2573
- * @inheritDoc
2574
- * @deprecated Use `Sentry.setTags()` instead.
2575
- */
2576
- setTags(tags) {
2577
- // eslint-disable-next-line deprecation/deprecation
2578
- this.getIsolationScope().setTags(tags);
2579
- }
2580
-
2581
- /**
2582
- * @inheritDoc
2583
- * @deprecated Use `Sentry.setExtras()` instead.
2584
- */
2585
- setExtras(extras) {
2586
- // eslint-disable-next-line deprecation/deprecation
2587
- this.getIsolationScope().setExtras(extras);
2588
- }
2589
-
2590
- /**
2591
- * @inheritDoc
2592
- * @deprecated Use `Sentry.setTag()` instead.
2593
- */
2594
- setTag(key, value) {
2595
- // eslint-disable-next-line deprecation/deprecation
2596
- this.getIsolationScope().setTag(key, value);
2597
- }
2598
-
2599
- /**
2600
- * @inheritDoc
2601
- * @deprecated Use `Sentry.setExtra()` instead.
2602
- */
2603
- setExtra(key, extra) {
2604
- // eslint-disable-next-line deprecation/deprecation
2605
- this.getIsolationScope().setExtra(key, extra);
2606
- }
2607
-
2608
- /**
2609
- * @inheritDoc
2610
- * @deprecated Use `Sentry.setContext()` instead.
2611
- */
2612
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
2613
- setContext(name, context) {
2614
- // eslint-disable-next-line deprecation/deprecation
2615
- this.getIsolationScope().setContext(name, context);
2616
- }
2617
-
2618
- /**
2619
- * @inheritDoc
2620
- * @deprecated Use `Sentry.getClient().getIntegrationByName()` instead.
2621
- */
2622
- getIntegration(integration) {
2623
- // eslint-disable-next-line deprecation/deprecation
2624
- const client = this.getClient();
2625
- if (!client) return null;
2626
- try {
2627
- // eslint-disable-next-line deprecation/deprecation
2628
- return client.getIntegration(integration);
2629
- } catch (_oO) {
2630
- DEBUG_BUILD$1 && logger.warn(`Cannot retrieve integration ${integration.id} from the current Hub`);
2631
- return null;
2632
- }
2633
- }
2634
-
2635
- /**
2636
- * @inheritDoc
2637
- *
2638
- * @deprecated Use top level `captureSession` instead.
2639
- */
2640
- captureSession(endSession = false) {
2641
- // both send the update and pull the session from the scope
2642
- if (endSession) {
2643
- // eslint-disable-next-line deprecation/deprecation
2644
- return this.endSession();
2645
- }
2646
-
2647
- // only send the update
2648
- this._sendSessionUpdate();
2649
- }
2650
-
2651
- /**
2652
- * @inheritDoc
2653
- * @deprecated Use top level `endSession` instead.
2654
- */
2655
- endSession() {
2656
- // eslint-disable-next-line deprecation/deprecation
2657
- const layer = this.getStackTop();
2658
- const scope = layer.scope;
2659
- const session = scope.getSession();
2660
- if (session) {
2661
- closeSession(session);
2662
- }
2663
- this._sendSessionUpdate();
2664
-
2665
- // the session is over; take it off of the scope
2666
- scope.setSession();
2667
- }
2668
-
2669
- /**
2670
- * @inheritDoc
2671
- * @deprecated Use top level `startSession` instead.
2672
- */
2673
- startSession(context) {
2674
- // eslint-disable-next-line deprecation/deprecation
2675
- const { scope, client } = this.getStackTop();
2676
- const { release, environment = DEFAULT_ENVIRONMENT } = (client && client.getOptions()) || {};
2677
-
2678
- // Will fetch userAgent if called from browser sdk
2679
- const { userAgent } = GLOBAL_OBJ.navigator || {};
2680
-
2681
- const session = makeSession({
2682
- release,
2683
- environment,
2684
- user: scope.getUser(),
2685
- ...(userAgent && { userAgent }),
2686
- ...context,
2687
- });
2688
-
2689
- // End existing session if there's one
2690
- const currentSession = scope.getSession && scope.getSession();
2691
- if (currentSession && currentSession.status === 'ok') {
2692
- updateSession(currentSession, { status: 'exited' });
2693
- }
2694
- // eslint-disable-next-line deprecation/deprecation
2695
- this.endSession();
2696
-
2697
- // Afterwards we set the new session on the scope
2698
- scope.setSession(session);
2699
-
2700
- return session;
2701
- }
2702
-
2703
- /**
2704
- * Returns if default PII should be sent to Sentry and propagated in ourgoing requests
2705
- * when Tracing is used.
2706
- *
2707
- * @deprecated Use top-level `getClient().getOptions().sendDefaultPii` instead. This function
2708
- * only unnecessarily increased API surface but only wrapped accessing the option.
2709
- */
2710
- shouldSendDefaultPii() {
2711
- // eslint-disable-next-line deprecation/deprecation
2712
- const client = this.getClient();
2713
- const options = client && client.getOptions();
2714
- return Boolean(options && options.sendDefaultPii);
2715
- }
2716
-
2717
- /**
2718
- * Sends the current Session on the scope
2719
- */
2720
- _sendSessionUpdate() {
2721
- // eslint-disable-next-line deprecation/deprecation
2722
- const { scope, client } = this.getStackTop();
2723
-
2724
- const session = scope.getSession();
2725
- if (session && client && client.captureSession) {
2726
- client.captureSession(session);
2727
- }
2728
- }
2729
-
2730
- /**
2731
- * Calls global extension method and binding current instance to the function call
2732
- */
2733
- // @ts-expect-error Function lacks ending return statement and return type does not include 'undefined'. ts(2366)
2734
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
2735
- _callExtensionMethod(method, ...args) {
2736
- const carrier = getMainCarrier();
2737
- const sentry = getSentryCarrier(carrier);
2738
- if (sentry.extensions && typeof sentry.extensions[method] === 'function') {
2739
- return sentry.extensions[method].apply(this, args);
2740
- }
2741
- DEBUG_BUILD$1 && logger.warn(`Extension method ${method} couldn't be found, doing nothing.`);
2742
- }
2743
- }
2744
-
2745
- /**
2746
- * Returns the default hub instance.
2747
- *
2748
- * If a hub is already registered in the global carrier but this module
2749
- * contains a more recent version, it replaces the registered version.
2750
- * Otherwise, the currently registered hub will be returned.
2751
- *
2752
- * @deprecated Use the respective replacement method directly instead.
2753
- */
2754
- function getCurrentHub() {
2755
- // Get main carrier (global for every environment)
2756
- const carrier = getMainCarrier();
2757
-
2758
- const acs = getAsyncContextStrategy(carrier);
2759
- return acs.getCurrentHub() || getGlobalHub();
2760
- }
2761
-
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();
3038
-
3039
- function ensureMetadataStacksAreParsed(parser) {
3040
- if (!GLOBAL_OBJ._sentryModuleMetadata) {
3041
- return;
3042
- }
3043
-
3044
- for (const stack of Object.keys(GLOBAL_OBJ._sentryModuleMetadata)) {
3045
- const metadata = GLOBAL_OBJ._sentryModuleMetadata[stack];
3046
-
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
- }
3064
- }
3065
- }
3066
-
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);
3076
- }
3077
-
3078
- /**
3079
- * Adds metadata to stack frames.
3080
- *
3081
- * Metadata is injected by the Sentry bundler plugins using the `_experiments.moduleMetadata` config option.
3082
- */
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
- }
3090
-
3091
- for (const frame of exception.stacktrace.frames || []) {
3092
- if (!frame.filename) {
3093
- continue;
3094
- }
3095
-
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
- }
3106
- }
3107
-
3108
- /**
3109
- * Strips metadata from stack frames.
3110
- */
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
- }
3118
-
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.
3125
- }
3126
- }
3127
-
3128
- const INTEGRATION_NAME$a = 'ModuleMetadata';
3129
-
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
- },
3148
-
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
- };
3214
-
3215
- return {
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;
3265
- },
3266
- };
3267
- }) ;
3268
-
3269
- const requestDataIntegration = defineIntegration(_requestDataIntegration);
3270
-
3271
- /**
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.
3275
- */
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
- }
3296
- }
3297
-
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;
3311
- }
3312
-
3313
- return {
3314
- include: {
3315
- ip,
3316
- user: addReqDataUserOpt,
3317
- request: requestIncludeKeys.length !== 0 ? requestIncludeKeys : undefined,
3318
- transaction: transactionNamingScheme,
3319
- },
3320
- };
3321
- }
3322
-
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
- }
3333
- }
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
-
3368
- /**
3369
- * Patch toString calls to return proper name for wrapped functions.
3370
- *
3371
- * ```js
3372
- * Sentry.init({
3373
- * integrations: [
3374
- * functionToStringIntegration(),
3375
- * ],
3376
- * });
3377
- * ```
3378
- */
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
- ) ;
3391
-
3392
- /**
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.
3394
- *
3395
- * ATTENTION: This constant must never cross package boundaries (i.e. be exported) to guarantee that it can be used for tree shaking.
3396
- */
3397
- const DEBUG_BUILD = (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__);
3398
-
3399
- const stdlib = detectLibc.familySync();
3400
- const platform = process.env['BUILD_PLATFORM'] || os.platform();
3401
- const arch = process.env['BUILD_ARCH'] || os.arch();
3402
- const abi = nodeAbi.getAbi(process$1.versions.node, 'node');
3403
- const identifier = [platform, arch, stdlib, abi].filter(c => c !== undefined && c !== null).join('-');
3404
- const built_from_source_path = path.resolve(__dirname, `./sentry_cpu_profiler-${identifier}`);
3405
- /**
3406
- * Imports cpp bindings based on the current platform and architecture.
3407
- */
3408
- // eslint-disable-next-line complexity
3409
- function importCppBindingsModule() {
3410
- // If a binary path is specified, use that.
3411
- if (process$1.env['SENTRY_PROFILER_BINARY_PATH']) {
3412
- const envPath = process$1.env['SENTRY_PROFILER_BINARY_PATH'];
3413
- return require(envPath);
3414
- }
3415
- // If a user specifies a different binary dir, they are in control of the binaries being moved there
3416
- if (process$1.env['SENTRY_PROFILER_BINARY_DIR']) {
3417
- const binaryPath = path.join(path.resolve(process$1.env['SENTRY_PROFILER_BINARY_DIR']), `sentry_cpu_profiler-${identifier}`);
3418
- return require(`${binaryPath}.node`);
3419
- }
3420
- // We need the fallthrough so that in the end, we can fallback to the require dynamice require.
3421
- // This is for cases where precompiled binaries were not provided, but may have been compiled from source.
3422
- if (platform === 'darwin') {
3423
- if (arch === 'x64') {
3424
- if (abi === '93') {
3425
- return require('./sentry_cpu_profiler-darwin-x64-93.node');
3426
- }
3427
- if (abi === '108') {
3428
- return require('./sentry_cpu_profiler-darwin-x64-108.node');
3429
- }
3430
- if (abi === '115') {
3431
- return require('./sentry_cpu_profiler-darwin-x64-115.node');
3432
- }
3433
- }
3434
- if (arch === 'arm64') {
3435
- if (abi === '93') {
3436
- return require('./sentry_cpu_profiler-darwin-arm64-93.node');
3437
- }
3438
- if (abi === '108') {
3439
- return require('./sentry_cpu_profiler-darwin-arm64-108.node');
3440
- }
3441
- if (abi === '115') {
3442
- return require('./sentry_cpu_profiler-darwin-arm64-115.node');
3443
- }
3444
- }
3445
- }
3446
- if (platform === 'win32') {
3447
- if (arch === 'x64') {
3448
- if (abi === '93') {
3449
- return require('./sentry_cpu_profiler-win32-x64-93.node');
3450
- }
3451
- if (abi === '108') {
3452
- return require('./sentry_cpu_profiler-win32-x64-108.node');
3453
- }
3454
- if (abi === '115') {
3455
- return require('./sentry_cpu_profiler-win32-x64-115.node');
3456
- }
3457
- }
3458
- }
3459
- if (platform === 'linux') {
3460
- if (arch === 'x64') {
3461
- if (stdlib === 'musl') {
3462
- if (abi === '93') {
3463
- return require('./sentry_cpu_profiler-linux-x64-musl-93.node');
3464
- }
3465
- if (abi === '108') {
3466
- return require('./sentry_cpu_profiler-linux-x64-musl-108.node');
3467
- }
3468
- if (abi === '115') {
3469
- return require('./sentry_cpu_profiler-linux-x64-musl-115.node');
3470
- }
3471
- }
3472
- if (stdlib === 'glibc') {
3473
- if (abi === '93') {
3474
- return require('./sentry_cpu_profiler-linux-x64-glibc-93.node');
3475
- }
3476
- if (abi === '108') {
3477
- return require('./sentry_cpu_profiler-linux-x64-glibc-108.node');
3478
- }
3479
- if (abi === '115') {
3480
- return require('./sentry_cpu_profiler-linux-x64-glibc-115.node');
3481
- }
3482
- }
3483
- }
3484
- if (arch === 'arm64') {
3485
- if (stdlib === 'musl') {
3486
- if (abi === '93') {
3487
- return require('./sentry_cpu_profiler-linux-arm64-musl-93.node');
3488
- }
3489
- if (abi === '108') {
3490
- return require('./sentry_cpu_profiler-linux-arm64-musl-108.node');
3491
- }
3492
- if (abi === '115') {
3493
- return require('./sentry_cpu_profiler-linux-arm64-musl-115.node');
3494
- }
3495
- }
3496
- if (stdlib === 'glibc') {
3497
- if (abi === '93') {
3498
- return require('./sentry_cpu_profiler-linux-arm64-glibc-93.node');
3499
- }
3500
- if (abi === '108') {
3501
- return require('./sentry_cpu_profiler-linux-arm64-glibc-108.node');
3502
- }
3503
- if (abi === '115') {
3504
- return require('./sentry_cpu_profiler-linux-arm64-glibc-115.node');
3505
- }
3506
- }
3507
- }
3508
- }
3509
- return require(`${built_from_source_path}.node`);
3510
- }
3511
- const PrivateCpuProfilerBindings = importCppBindingsModule();
3512
- const CpuProfilerBindings = {
3513
- startProfiling(name) {
3514
- if (!PrivateCpuProfilerBindings) {
3515
- DEBUG_BUILD && logger.log('[Profiling] Bindings not loaded, ignoring call to startProfiling.');
3516
- return;
3517
- }
3518
- return PrivateCpuProfilerBindings.startProfiling(name);
3519
- },
3520
- stopProfiling(name) {
3521
- if (!PrivateCpuProfilerBindings) {
3522
- DEBUG_BUILD &&
3523
- logger.log('[Profiling] Bindings not loaded or profile was never started, ignoring call to stopProfiling.');
3524
- return null;
3525
- }
3526
- return PrivateCpuProfilerBindings.stopProfiling(name, worker_threads.threadId, !!GLOBAL_OBJ._sentryDebugIds);
3527
- },
3528
- };
3529
-
3530
- // We require the file because if we import it, it will be included in the bundle.
3531
- // I guess tsc does not check file contents when it's imported.
3532
- const THREAD_ID_STRING = String(worker_threads.threadId);
3533
- const THREAD_NAME = worker_threads.isMainThread ? 'main' : 'worker';
3534
- const FORMAT_VERSION = '1';
3535
- // Os machine was backported to 16.18, but this was not reflected in the types
3536
- // @ts-expect-error ignore missing
3537
- const machine = typeof os.machine === 'function' ? os.machine() : os.arch();
3538
- // Machine properties (eval only once)
3539
- const PLATFORM = os.platform();
3540
- const RELEASE = os.release();
3541
- const VERSION = os.version();
3542
- const TYPE = os.type();
3543
- const MODEL = machine;
3544
- const ARCH = os.arch();
3545
- /**
3546
- * Checks if the profile is a raw profile or a profile enriched with thread information.
3547
- * @param {ThreadCpuProfile | RawThreadCpuProfile} profile
3548
- * @returns {boolean}
3549
- */
3550
- function isRawThreadCpuProfile(profile) {
3551
- return !('thread_metadata' in profile);
3552
- }
3553
- /**
3554
- * Enriches the profile with threadId of the current thread.
3555
- * This is done in node as we seem to not be able to get the info from C native code.
3556
- *
3557
- * @param {ThreadCpuProfile | RawThreadCpuProfile} profile
3558
- * @returns {ThreadCpuProfile}
3559
- */
3560
- function enrichWithThreadInformation(profile) {
3561
- if (!isRawThreadCpuProfile(profile)) {
3562
- return profile;
3563
- }
3564
- return {
3565
- samples: profile.samples,
3566
- frames: profile.frames,
3567
- stacks: profile.stacks,
3568
- thread_metadata: {
3569
- [THREAD_ID_STRING]: {
3570
- name: THREAD_NAME,
3571
- },
3572
- },
3573
- };
3574
- }
3575
- /**
3576
- * Creates a profiling envelope item, if the profile does not pass validation, returns null.
3577
- * @param {RawThreadCpuProfile}
3578
- * @param {Event}
3579
- * @returns {Profile | null}
3580
- */
3581
- function createProfilingEvent(profile, event) {
3582
- var _a, _b, _c, _d, _e, _f, _g;
3583
- if (!isValidProfile(profile)) {
3584
- return null;
3585
- }
3586
- return createProfilePayload(profile, {
3587
- release: (_a = event.release) !== null && _a !== void 0 ? _a : '',
3588
- environment: (_b = event.environment) !== null && _b !== void 0 ? _b : '',
3589
- event_id: (_c = event.event_id) !== null && _c !== void 0 ? _c : '',
3590
- transaction: (_d = event.transaction) !== null && _d !== void 0 ? _d : '',
3591
- start_timestamp: event.start_timestamp ? event.start_timestamp * 1000 : Date.now(),
3592
- 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 : '',
3593
- profile_id: profile.profile_id,
3594
- });
3595
- }
3596
- /**
3597
- * Create a profile
3598
- * @param {RawThreadCpuProfile} cpuProfile
3599
- * @param {options}
3600
- * @returns {Profile}
3601
- */
3602
- function createProfilePayload(cpuProfile, { release, environment, event_id, transaction, start_timestamp, trace_id, profile_id, }) {
3603
- // Log a warning if the profile has an invalid traceId (should be uuidv4).
3604
- // All profiles and transactions are rejected if this is the case and we want to
3605
- // warn users that this is happening if they enable debug flag
3606
- if (trace_id && trace_id.length !== 32) {
3607
- DEBUG_BUILD && logger.log(`[Profiling] Invalid traceId: ${trace_id} on profiled event`);
3608
- }
3609
- const enrichedThreadProfile = enrichWithThreadInformation(cpuProfile);
3610
- const profile = {
3611
- event_id: profile_id,
3612
- timestamp: new Date(start_timestamp).toISOString(),
3613
- platform: 'node',
3614
- version: FORMAT_VERSION,
3615
- release: release,
3616
- environment: environment,
3617
- measurements: cpuProfile.measurements,
3618
- runtime: {
3619
- name: 'node',
3620
- version: process$1.versions.node || '',
3621
- },
3622
- os: {
3623
- name: PLATFORM,
3624
- version: RELEASE,
3625
- build_number: VERSION,
3626
- },
3627
- device: {
3628
- locale: process$1.env['LC_ALL'] || process$1.env['LC_MESSAGES'] || process$1.env['LANG'] || process$1.env['LANGUAGE'] || '',
3629
- model: MODEL,
3630
- manufacturer: TYPE,
3631
- architecture: ARCH,
3632
- is_emulator: false,
3633
- },
3634
- debug_meta: {
3635
- images: applyDebugMetadata(cpuProfile.resources),
3636
- },
3637
- profile: enrichedThreadProfile,
3638
- transaction: {
3639
- name: transaction,
3640
- id: event_id,
3641
- trace_id: trace_id || '',
3642
- active_thread_id: THREAD_ID_STRING,
3643
- },
3644
- };
3645
- return profile;
3646
- }
3647
- /**
3648
- * Checks the given sample rate to make sure it is valid type and value (a boolean, or a number between 0 and 1).
3649
- * @param {unknown} rate
3650
- * @returns {boolean}
3651
- */
3652
- function isValidSampleRate(rate) {
3653
- // we need to check NaN explicitly because it's of type 'number' and therefore wouldn't get caught by this typecheck
3654
- if ((typeof rate !== 'number' && typeof rate !== 'boolean') || (typeof rate === 'number' && isNaN(rate))) {
3655
- DEBUG_BUILD &&
3656
- logger.warn(`[Profiling] Invalid sample rate. Sample rate must be a boolean or a number between 0 and 1. Got ${JSON.stringify(rate)} of type ${JSON.stringify(typeof rate)}.`);
3657
- return false;
3658
- }
3659
- // Boolean sample rates are always valid
3660
- if (rate === true || rate === false) {
3661
- return true;
3662
- }
3663
- // in case sampleRate is a boolean, it will get automatically cast to 1 if it's true and 0 if it's false
3664
- if (rate < 0 || rate > 1) {
3665
- DEBUG_BUILD && logger.warn(`[Profiling] Invalid sample rate. Sample rate must be between 0 and 1. Got ${rate}.`);
3666
- return false;
3667
- }
3668
- return true;
3669
- }
3670
- /**
3671
- * Checks if the profile is valid and can be sent to Sentry.
3672
- * @param {RawThreadCpuProfile} profile
3673
- * @returns {boolean}
3674
- */
3675
- function isValidProfile(profile) {
3676
- if (profile.samples.length <= 1) {
3677
- DEBUG_BUILD &&
3678
- // Log a warning if the profile has less than 2 samples so users can know why
3679
- // they are not seeing any profiling data and we cant avoid the back and forth
3680
- // of asking them to provide us with a dump of the profile data.
3681
- logger.log('[Profiling] Discarding profile because it contains less than 2 samples');
3682
- return false;
3683
- }
3684
- if (!profile.profile_id) {
3685
- return false;
3686
- }
3687
- return true;
3688
- }
3689
- /**
3690
- * Adds items to envelope if they are not already present - mutates the envelope.
3691
- * @param {Envelope} envelope
3692
- * @param {Profile[]} profiles
3693
- * @returns {Envelope}
3694
- */
3695
- function addProfilesToEnvelope(envelope, profiles) {
3696
- if (!profiles.length) {
3697
- return envelope;
3698
- }
3699
- for (const profile of profiles) {
3700
- // @ts-expect-error untyped envelope
3701
- envelope[1].push([{ type: 'profile' }, profile]);
3702
- }
3703
- return envelope;
3704
- }
3705
- /**
3706
- * Finds transactions with profile_id context in the envelope
3707
- * @param {Envelope} envelope
3708
- * @returns {Event[]}
3709
- */
3710
- function findProfiledTransactionsFromEnvelope(envelope) {
3711
- const events = [];
3712
- forEachEnvelopeItem(envelope, (item, type) => {
3713
- var _a, _b;
3714
- if (type !== 'transaction') {
3715
- return;
3716
- }
3717
- // First item is the type, so we can skip it, everything else is an event
3718
- for (let j = 1; j < item.length; j++) {
3719
- const event = item[j];
3720
- if (!event) {
3721
- // Shouldnt happen, but lets be safe
3722
- continue;
3723
- }
3724
- // @ts-expect-error profile_id is not part of the metadata type
3725
- const profile_id = (_b = (_a = event.contexts) === null || _a === void 0 ? void 0 : _a['profile']) === null || _b === void 0 ? void 0 : _b['profile_id'];
3726
- if (event && profile_id) {
3727
- events.push(item[j]);
3728
- }
3729
- }
3730
- });
3731
- return events;
3732
- }
3733
- const debugIdStackParserCache = new WeakMap();
3734
- /**
3735
- * Cross reference profile collected resources with debug_ids and return a list of debug images.
3736
- * @param {string[]} resource_paths
3737
- * @returns {DebugImage[]}
3738
- */
3739
- function applyDebugMetadata(resource_paths) {
3740
- const debugIdMap = GLOBAL_OBJ._sentryDebugIds;
3741
- if (!debugIdMap) {
3742
- return [];
3743
- }
3744
- // eslint-disable-next-line deprecation/deprecation
3745
- const hub = getCurrentHub();
3746
- // eslint-disable-next-line deprecation/deprecation
3747
- const client = hub.getClient();
3748
- const options = client && client.getOptions();
3749
- if (!options || !options.stackParser) {
3750
- return [];
3751
- }
3752
- let debugIdStackFramesCache;
3753
- const cachedDebugIdStackFrameCache = debugIdStackParserCache.get(options.stackParser);
3754
- if (cachedDebugIdStackFrameCache) {
3755
- debugIdStackFramesCache = cachedDebugIdStackFrameCache;
3756
- }
3757
- else {
3758
- debugIdStackFramesCache = new Map();
3759
- debugIdStackParserCache.set(options.stackParser, debugIdStackFramesCache);
3760
- }
3761
- // Build a map of filename -> debug_id.
3762
- const filenameDebugIdMap = Object.keys(debugIdMap).reduce((acc, debugIdStackTrace) => {
3763
- let parsedStack;
3764
- const cachedParsedStack = debugIdStackFramesCache.get(debugIdStackTrace);
3765
- if (cachedParsedStack) {
3766
- parsedStack = cachedParsedStack;
3767
- }
3768
- else {
3769
- parsedStack = options.stackParser(debugIdStackTrace);
3770
- debugIdStackFramesCache.set(debugIdStackTrace, parsedStack);
3771
- }
3772
- for (let i = parsedStack.length - 1; i >= 0; i--) {
3773
- const stackFrame = parsedStack[i];
3774
- const file = stackFrame && stackFrame.filename;
3775
- if (stackFrame && file) {
3776
- acc[file] = debugIdMap[debugIdStackTrace];
3777
- break;
3778
- }
3779
- }
3780
- return acc;
3781
- }, {});
3782
- const images = [];
3783
- for (const resource of resource_paths) {
3784
- if (resource && filenameDebugIdMap[resource]) {
3785
- images.push({
3786
- type: 'sourcemap',
3787
- code_file: resource,
3788
- debug_id: filenameDebugIdMap[resource],
3789
- });
3790
- }
3791
- }
3792
- return images;
3793
- }
3794
-
3795
- const MAX_PROFILE_DURATION_MS = 30 * 1000;
3796
- /**
3797
- * Takes a transaction and determines if it should be profiled or not. If it should be profiled, it returns the
3798
- * profile_id, otherwise returns undefined. Takes care of setting profile context on transaction as well
3799
- */
3800
- function maybeProfileSpan(client, span, customSamplingContext) {
3801
- // profilesSampleRate is multiplied with tracesSampleRate to get the final sampling rate. We dont perform
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)) {
3805
- return;
3806
- }
3807
- // Client and options are required for profiling
3808
- if (!client) {
3809
- DEBUG_BUILD && logger.log('[Profiling] Profiling disabled, no client found.');
3810
- return;
3811
- }
3812
- const options = client.getOptions();
3813
- if (!options) {
3814
- DEBUG_BUILD && logger.log('[Profiling] Profiling disabled, no options found.');
3815
- return;
3816
- }
3817
- const profilesSampler = options.profilesSampler;
3818
- let profilesSampleRate = options.profilesSampleRate;
3819
- // Prefer sampler to sample rate if both are provided.
3820
- if (typeof profilesSampler === 'function') {
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));
3828
- }
3829
- // Since this is coming from the user (or from a function provided by the user), who knows what we might get. (The
3830
- // only valid values are booleans or numbers between 0 and 1.)
3831
- if (!isValidSampleRate(profilesSampleRate)) {
3832
- DEBUG_BUILD && logger.warn('[Profiling] Discarding profile because of invalid sample rate.');
3833
- return;
3834
- }
3835
- // if the function returned 0 (or false), or if `profileSampleRate` is 0, it's a sign the profile should be dropped
3836
- if (!profilesSampleRate) {
3837
- DEBUG_BUILD &&
3838
- logger.log(`[Profiling] Discarding profile because ${typeof profilesSampler === 'function'
3839
- ? 'profileSampler returned 0 or false'
3840
- : 'a negative sampling decision was inherited or profileSampleRate is set to 0'}`);
3841
- return;
3842
- }
3843
- // Now we roll the dice. Math.random is inclusive of 0, but not of 1, so strict < is safe here. In case sampleRate is
3844
- // a boolean, the < comparison will cause it to be automatically cast to 1 if it's true and 0 if it's false.
3845
- const sampled = profilesSampleRate === true ? true : Math.random() < profilesSampleRate;
3846
- // Check if we should sample this profile
3847
- if (!sampled) {
3848
- DEBUG_BUILD &&
3849
- logger.log(`[Profiling] Discarding profile because it's not included in the random sample (sampling rate = ${Number(profilesSampleRate)})`);
3850
- return;
3851
- }
3852
- const profile_id = uuid4();
3853
- CpuProfilerBindings.startProfiling(profile_id);
3854
- DEBUG_BUILD && logger.log(`[Profiling] started profiling transaction: ${spanToJSON(span).description}`);
3855
- // set transaction context - do this regardless if profiling fails down the line
3856
- // so that we can still see the profile_id in the transaction context
3857
- return profile_id;
3858
- }
3859
- /**
3860
- * Stops the profiler for profile_id and returns the profile
3861
- * @param transaction
3862
- * @param profile_id
3863
- * @returns
3864
- */
3865
- function stopSpanProfile(span, profile_id) {
3866
- // Should not happen, but satisfy the type checker and be safe regardless.
3867
- if (!profile_id) {
3868
- return null;
3869
- }
3870
- const profile = CpuProfilerBindings.stopProfiling(profile_id);
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.
3873
- if (!profile) {
3874
- DEBUG_BUILD &&
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');
3876
- return null;
3877
- }
3878
- // Assign profile_id to the profile
3879
- profile.profile_id = profile_id;
3880
- return profile;
3881
- }
3882
-
3883
- const MAX_PROFILE_QUEUE_LENGTH = 50;
3884
- const PROFILE_QUEUE = [];
3885
- const PROFILE_TIMEOUTS = {};
3886
- function addToProfileQueue(profile) {
3887
- PROFILE_QUEUE.push(profile);
3888
- // We only want to keep the last n profiles in the queue.
3889
- if (PROFILE_QUEUE.length > MAX_PROFILE_QUEUE_LENGTH) {
3890
- PROFILE_QUEUE.shift();
3891
- }
3892
- }
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);
3904
- if (profile_id) {
3905
- const options = client.getOptions();
3906
- // Not intended for external use, hence missing types, but we want to profile a couple of things at Sentry that
3907
- // currently exceed the default timeout set by the SDKs.
3908
- const maxProfileDurationMs = (options._experiments && options._experiments['maxProfileDurationMs']) || MAX_PROFILE_DURATION_MS;
3909
- if (PROFILE_TIMEOUTS[profile_id]) {
3910
- global.clearTimeout(PROFILE_TIMEOUTS[profile_id]);
3911
- // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
3912
- delete PROFILE_TIMEOUTS[profile_id];
3913
- }
3914
- // Enqueue a timeout to prevent profiles from running over max duration.
3915
- PROFILE_TIMEOUTS[profile_id] = global.setTimeout(() => {
3916
- DEBUG_BUILD &&
3917
- logger.log('[Profiling] max profile duration elapsed, stopping profiling for:', spanToJSON(span).description);
3918
- const profile = stopSpanProfile(span, profile_id);
3919
- if (profile) {
3920
- addToProfileQueue(profile);
3921
- }
3922
- }, maxProfileDurationMs);
3923
- getCurrentScope().setContext('profile', { profile_id });
3924
- spanToProfileIdMap.set(span, profile_id);
3925
- }
3926
- });
3927
- client.on('spanEnd', span => {
3928
- const profile_id = spanToProfileIdMap.get(span);
3929
- if (profile_id) {
3930
- if (PROFILE_TIMEOUTS[profile_id]) {
3931
- global.clearTimeout(PROFILE_TIMEOUTS[profile_id]);
3932
- // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
3933
- delete PROFILE_TIMEOUTS[profile_id];
3934
- }
3935
- const profile = stopSpanProfile(span, profile_id);
3936
- if (profile) {
3937
- addToProfileQueue(profile);
3938
- }
3939
- }
3940
- });
3941
- client.on('beforeEnvelope', (envelope) => {
3942
- var _a, _b;
3943
- // if not profiles are in queue, there is nothing to add to the envelope.
3944
- if (!PROFILE_QUEUE.length) {
3945
- return;
3946
- }
3947
- const profiledTransactionEvents = findProfiledTransactionsFromEnvelope(envelope);
3948
- if (!profiledTransactionEvents.length) {
3949
- return;
3950
- }
3951
- const profilesToAddToEnvelope = [];
3952
- for (const profiledTransaction of profiledTransactionEvents) {
3953
- const profileContext = (_a = profiledTransaction.contexts) === null || _a === void 0 ? void 0 : _a['profile'];
3954
- const profile_id = profileContext === null || profileContext === void 0 ? void 0 : profileContext['profile_id'];
3955
- if (!profile_id) {
3956
- throw new TypeError('[Profiling] cannot find profile for a transaction without a profile context');
3957
- }
3958
- // Remove the profile from the transaction context before sending, relay will take care of the rest.
3959
- if (profileContext) {
3960
- (_b = profiledTransaction.contexts) === null || _b === void 0 ? true : delete _b['profile'];
3961
- }
3962
- // We need to find both a profile and a transaction event for the same profile_id.
3963
- const profileIndex = PROFILE_QUEUE.findIndex(p => p.profile_id === profile_id);
3964
- if (profileIndex === -1) {
3965
- DEBUG_BUILD && logger.log(`[Profiling] Could not retrieve profile for transaction: ${profile_id}`);
3966
- continue;
3967
- }
3968
- const cpuProfile = PROFILE_QUEUE[profileIndex];
3969
- if (!cpuProfile) {
3970
- DEBUG_BUILD && logger.log(`[Profiling] Could not retrieve profile for transaction: ${profile_id}`);
3971
- continue;
3972
- }
3973
- // Remove the profile from the queue.
3974
- PROFILE_QUEUE.splice(profileIndex, 1);
3975
- const profile = createProfilingEvent(cpuProfile, profiledTransaction);
3976
- if (client.emit && profile) {
3977
- const integrations = client['_integrations'] && client['_integrations'] !== null && !Array.isArray(client['_integrations'])
3978
- ? Object.keys(client['_integrations'])
3979
- : undefined;
3980
- // @ts-expect-error bad overload due to unknown event
3981
- client.emit('preprocessEvent', profile, {
3982
- event_id: profiledTransaction.event_id,
3983
- integrations,
3984
- });
3985
- }
3986
- if (profile) {
3987
- profilesToAddToEnvelope.push(profile);
3988
- }
3989
- }
3990
- addProfilesToEnvelope(envelope, profilesToAddToEnvelope);
3991
- });
3992
- },
3993
- };
3994
- });
3995
- /**
3996
- * We need this integration in order to send data to Sentry. We hook into the event processor
3997
- * and inspect each event to see if it is a transaction event and if that transaction event
3998
- * contains a profile on it's metadata. If that is the case, we create a profiling event envelope
3999
- * and delete the profile from the transaction metadata.
4000
- */
4001
- const nodeProfilingIntegration = defineIntegration(_nodeProfilingIntegration);
4002
-
4003
- exports.nodeProfilingIntegration = nodeProfilingIntegration;
4004
- //# sourceMappingURL=index.js.map