@myinterview/widget-react 1.1.23-binary-check-one → 1.1.23-binary-002

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/dist/esm/index.js CHANGED
@@ -2,6 +2,31 @@ import * as React from 'react';
2
2
  import React__default, { createContext, forwardRef, useRef, useEffect, useState, useLayoutEffect, useImperativeHandle, useDebugValue, useContext, createElement, useCallback, useMemo } from 'react';
3
3
  import { createPortal } from 'react-dom';
4
4
 
5
+ /*! *****************************************************************************
6
+ Copyright (c) Microsoft Corporation.
7
+
8
+ Permission to use, copy, modify, and/or distribute this software for any
9
+ purpose with or without fee is hereby granted.
10
+
11
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
12
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
14
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
16
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17
+ PERFORMANCE OF THIS SOFTWARE.
18
+ ***************************************************************************** */
19
+
20
+ function __awaiter(thisArg, _arguments, P, generator) {
21
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
22
+ return new (P || (P = Promise))(function (resolve, reject) {
23
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
24
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
25
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
26
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
27
+ });
28
+ }
29
+
5
30
  // eslint-disable-next-line @typescript-eslint/unbound-method
6
31
  const objectToString = Object.prototype.toString;
7
32
 
@@ -370,83 +395,19 @@ function getLocationHref() {
370
395
  }
371
396
  }
372
397
 
373
- /** Prefix for logging strings */
374
- const PREFIX = 'Sentry Logger ';
375
-
376
- const CONSOLE_LEVELS = ['debug', 'info', 'warn', 'error', 'log', 'assert', 'trace'] ;
377
-
378
- /**
379
- * Temporarily disable sentry console instrumentations.
380
- *
381
- * @param callback The function to run against the original `console` messages
382
- * @returns The results of the callback
383
- */
384
- function consoleSandbox(callback) {
385
- if (!('console' in GLOBAL_OBJ)) {
386
- return callback();
387
- }
388
-
389
- const originalConsole = GLOBAL_OBJ.console ;
390
- const wrappedLevels = {};
391
-
392
- // Restore all wrapped console methods
393
- CONSOLE_LEVELS.forEach(level => {
394
- // TODO(v7): Remove this check as it's only needed for Node 6
395
- const originalWrappedFunc =
396
- originalConsole[level] && (originalConsole[level] ).__sentry_original__;
397
- if (level in originalConsole && originalWrappedFunc) {
398
- wrappedLevels[level] = originalConsole[level] ;
399
- originalConsole[level] = originalWrappedFunc ;
400
- }
401
- });
402
-
403
- try {
404
- return callback();
405
- } finally {
406
- // Revert restoration to wrapped state
407
- Object.keys(wrappedLevels).forEach(level => {
408
- originalConsole[level] = wrappedLevels[level ];
409
- });
410
- }
411
- }
412
-
413
- function makeLogger() {
414
- let enabled = false;
415
- const logger = {
416
- enable: () => {
417
- enabled = true;
418
- },
419
- disable: () => {
420
- enabled = false;
421
- },
422
- };
398
+ /** An error emitted by Sentry SDKs and related utilities. */
399
+ class SentryError extends Error {
400
+ /** Display name of this error instance. */
423
401
 
424
- if ((typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__)) {
425
- CONSOLE_LEVELS.forEach(name => {
426
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
427
- logger[name] = (...args) => {
428
- if (enabled) {
429
- consoleSandbox(() => {
430
- GLOBAL_OBJ.console[name](`${PREFIX}[${name}]:`, ...args);
431
- });
432
- }
433
- };
434
- });
435
- } else {
436
- CONSOLE_LEVELS.forEach(name => {
437
- logger[name] = () => undefined;
438
- });
402
+ constructor( message, logLevel = 'warn') {
403
+ super(message);this.message = message;
404
+ this.name = new.target.prototype.constructor.name;
405
+ // This sets the prototype to be `Error`, not `SentryError`. It's unclear why we do this, but commenting this line
406
+ // out causes various (seemingly totally unrelated) playwright tests consistently time out. FYI, this makes
407
+ // instances of `SentryError` fail `obj instanceof SentryError` checks.
408
+ Object.setPrototypeOf(this, new.target.prototype);
409
+ this.logLevel = logLevel;
439
410
  }
440
-
441
- return logger ;
442
- }
443
-
444
- // Ensure we only have a single logger instance, even if multiple versions of @sentry/utils are being used
445
- let logger;
446
- if ((typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__)) {
447
- logger = getGlobalSingleton('logger', makeLogger);
448
- } else {
449
- logger = makeLogger();
450
411
  }
451
412
 
452
413
  /** Regular expression used to parse a Dsn. */
@@ -477,16 +438,13 @@ function dsnToString(dsn, withPassword = false) {
477
438
  * Parses a Dsn from a given string.
478
439
  *
479
440
  * @param str A Dsn as string
480
- * @returns Dsn as DsnComponents or undefined if @param str is not a valid DSN string
441
+ * @returns Dsn as DsnComponents
481
442
  */
482
443
  function dsnFromString(str) {
483
444
  const match = DSN_REGEX.exec(str);
484
445
 
485
446
  if (!match) {
486
- // This should be logged to the console
487
- // eslint-disable-next-line no-console
488
- console.error(`Invalid Sentry Dsn: ${str}`);
489
- return undefined;
447
+ throw new SentryError(`Invalid Sentry Dsn: ${str}`);
490
448
  }
491
449
 
492
450
  const [protocol, publicKey, pass = '', host, port = '', lastPath] = match.slice(1);
@@ -523,69 +481,119 @@ function dsnFromComponents(components) {
523
481
 
524
482
  function validateDsn(dsn) {
525
483
  if (!(typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__)) {
526
- return true;
484
+ return;
527
485
  }
528
486
 
529
487
  const { port, projectId, protocol } = dsn;
530
488
 
531
489
  const requiredComponents = ['protocol', 'publicKey', 'host', 'projectId'];
532
- const hasMissingRequiredComponent = requiredComponents.find(component => {
490
+ requiredComponents.forEach(component => {
533
491
  if (!dsn[component]) {
534
- logger.error(`Invalid Sentry Dsn: ${component} missing`);
535
- return true;
492
+ throw new SentryError(`Invalid Sentry Dsn: ${component} missing`);
536
493
  }
537
- return false;
538
494
  });
539
495
 
540
- if (hasMissingRequiredComponent) {
541
- return false;
542
- }
543
-
544
496
  if (!projectId.match(/^\d+$/)) {
545
- logger.error(`Invalid Sentry Dsn: Invalid projectId ${projectId}`);
546
- return false;
497
+ throw new SentryError(`Invalid Sentry Dsn: Invalid projectId ${projectId}`);
547
498
  }
548
499
 
549
500
  if (!isValidProtocol(protocol)) {
550
- logger.error(`Invalid Sentry Dsn: Invalid protocol ${protocol}`);
551
- return false;
501
+ throw new SentryError(`Invalid Sentry Dsn: Invalid protocol ${protocol}`);
552
502
  }
553
503
 
554
504
  if (port && isNaN(parseInt(port, 10))) {
555
- logger.error(`Invalid Sentry Dsn: Invalid port ${port}`);
556
- return false;
505
+ throw new SentryError(`Invalid Sentry Dsn: Invalid port ${port}`);
557
506
  }
558
507
 
559
508
  return true;
560
509
  }
561
510
 
562
- /**
563
- * Creates a valid Sentry Dsn object, identifying a Sentry instance and project.
564
- * @returns a valid DsnComponents object or `undefined` if @param from is an invalid DSN source
565
- */
511
+ /** The Sentry Dsn, identifying a Sentry instance and project. */
566
512
  function makeDsn(from) {
567
513
  const components = typeof from === 'string' ? dsnFromString(from) : dsnFromComponents(from);
568
- if (!components || !validateDsn(components)) {
569
- return undefined;
570
- }
514
+ validateDsn(components);
571
515
  return components;
572
516
  }
573
517
 
574
- /** An error emitted by Sentry SDKs and related utilities. */
575
- class SentryError extends Error {
576
- /** Display name of this error instance. */
518
+ /** Prefix for logging strings */
519
+ const PREFIX = 'Sentry Logger ';
577
520
 
578
- constructor( message, logLevel = 'warn') {
579
- super(message);this.message = message;
580
- this.name = new.target.prototype.constructor.name;
581
- // This sets the prototype to be `Error`, not `SentryError`. It's unclear why we do this, but commenting this line
582
- // out causes various (seemingly totally unrelated) playwright tests consistently time out. FYI, this makes
583
- // instances of `SentryError` fail `obj instanceof SentryError` checks.
584
- Object.setPrototypeOf(this, new.target.prototype);
585
- this.logLevel = logLevel;
521
+ const CONSOLE_LEVELS = ['debug', 'info', 'warn', 'error', 'log', 'assert', 'trace'] ;
522
+
523
+ /**
524
+ * Temporarily disable sentry console instrumentations.
525
+ *
526
+ * @param callback The function to run against the original `console` messages
527
+ * @returns The results of the callback
528
+ */
529
+ function consoleSandbox(callback) {
530
+ if (!('console' in GLOBAL_OBJ)) {
531
+ return callback();
532
+ }
533
+
534
+ const originalConsole = GLOBAL_OBJ.console ;
535
+ const wrappedLevels = {};
536
+
537
+ // Restore all wrapped console methods
538
+ CONSOLE_LEVELS.forEach(level => {
539
+ // TODO(v7): Remove this check as it's only needed for Node 6
540
+ const originalWrappedFunc =
541
+ originalConsole[level] && (originalConsole[level] ).__sentry_original__;
542
+ if (level in originalConsole && originalWrappedFunc) {
543
+ wrappedLevels[level] = originalConsole[level] ;
544
+ originalConsole[level] = originalWrappedFunc ;
545
+ }
546
+ });
547
+
548
+ try {
549
+ return callback();
550
+ } finally {
551
+ // Revert restoration to wrapped state
552
+ Object.keys(wrappedLevels).forEach(level => {
553
+ originalConsole[level] = wrappedLevels[level ];
554
+ });
586
555
  }
587
556
  }
588
557
 
558
+ function makeLogger() {
559
+ let enabled = false;
560
+ const logger = {
561
+ enable: () => {
562
+ enabled = true;
563
+ },
564
+ disable: () => {
565
+ enabled = false;
566
+ },
567
+ };
568
+
569
+ if ((typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__)) {
570
+ CONSOLE_LEVELS.forEach(name => {
571
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
572
+ logger[name] = (...args) => {
573
+ if (enabled) {
574
+ consoleSandbox(() => {
575
+ GLOBAL_OBJ.console[name](`${PREFIX}[${name}]:`, ...args);
576
+ });
577
+ }
578
+ };
579
+ });
580
+ } else {
581
+ CONSOLE_LEVELS.forEach(name => {
582
+ logger[name] = () => undefined;
583
+ });
584
+ }
585
+
586
+ return logger ;
587
+ }
588
+
589
+ // Ensure we only have a single logger instance, even if multiple versions of @sentry/utils are being used
590
+ let logger;
591
+ if ((typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__)) {
592
+ logger = getGlobalSingleton('logger', makeLogger);
593
+ } else {
594
+ logger = makeLogger();
595
+ }
596
+
589
597
  /**
590
598
  * Truncates given string to the maximum characters count
591
599
  *
@@ -939,12 +947,6 @@ function createStackParser(...parsers) {
939
947
  // Remove webpack (error: *) wrappers
940
948
  const cleanedLine = WEBPACK_ERROR_REGEXP.test(line) ? line.replace(WEBPACK_ERROR_REGEXP, '$1') : line;
941
949
 
942
- // https://github.com/getsentry/sentry-javascript/issues/7813
943
- // Skip Error: lines
944
- if (cleanedLine.match(/\S*Error: /)) {
945
- continue;
946
- }
947
-
948
950
  for (const parser of sortedParsers) {
949
951
  const frame = parser(cleanedLine);
950
952
 
@@ -1129,8 +1131,6 @@ function supportsHistory() {
1129
1131
  // eslint-disable-next-line deprecation/deprecation
1130
1132
  const WINDOW$3 = getGlobalObject();
1131
1133
 
1132
- const SENTRY_XHR_DATA_KEY = '__sentry_xhr_v2__';
1133
-
1134
1134
  /**
1135
1135
  * Instrument native APIs to call handlers that can be used to create breadcrumbs, APM spans etc.
1136
1136
  * - Console API
@@ -1243,13 +1243,11 @@ function instrumentFetch() {
1243
1243
 
1244
1244
  fill(WINDOW$3, 'fetch', function (originalFetch) {
1245
1245
  return function (...args) {
1246
- const { method, url } = parseFetchArgs(args);
1247
-
1248
1246
  const handlerData = {
1249
1247
  args,
1250
1248
  fetchData: {
1251
- method,
1252
- url,
1249
+ method: getFetchMethod(args),
1250
+ url: getFetchUrl(args),
1253
1251
  },
1254
1252
  startTimestamp: Date.now(),
1255
1253
  };
@@ -1284,53 +1282,29 @@ function instrumentFetch() {
1284
1282
  });
1285
1283
  }
1286
1284
 
1287
- function hasProp(obj, prop) {
1288
- return !!obj && typeof obj === 'object' && !!(obj )[prop];
1289
- }
1290
-
1291
- function getUrlFromResource(resource) {
1292
- if (typeof resource === 'string') {
1293
- return resource;
1294
- }
1295
-
1296
- if (!resource) {
1297
- return '';
1298
- }
1299
-
1300
- if (hasProp(resource, 'url')) {
1301
- return resource.url;
1285
+ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
1286
+ /** Extract `method` from fetch call arguments */
1287
+ function getFetchMethod(fetchArgs = []) {
1288
+ if ('Request' in WINDOW$3 && isInstanceOf(fetchArgs[0], Request) && fetchArgs[0].method) {
1289
+ return String(fetchArgs[0].method).toUpperCase();
1302
1290
  }
1303
-
1304
- if (resource.toString) {
1305
- return resource.toString();
1291
+ if (fetchArgs[1] && fetchArgs[1].method) {
1292
+ return String(fetchArgs[1].method).toUpperCase();
1306
1293
  }
1307
-
1308
- return '';
1294
+ return 'GET';
1309
1295
  }
1310
1296
 
1311
- /**
1312
- * Parses the fetch arguments to find the used Http method and the url of the request
1313
- */
1314
- function parseFetchArgs(fetchArgs) {
1315
- if (fetchArgs.length === 0) {
1316
- return { method: 'GET', url: '' };
1297
+ /** Extract `url` from fetch call arguments */
1298
+ function getFetchUrl(fetchArgs = []) {
1299
+ if (typeof fetchArgs[0] === 'string') {
1300
+ return fetchArgs[0];
1317
1301
  }
1318
-
1319
- if (fetchArgs.length === 2) {
1320
- const [url, options] = fetchArgs ;
1321
-
1322
- return {
1323
- url: getUrlFromResource(url),
1324
- method: hasProp(options, 'method') ? String(options.method).toUpperCase() : 'GET',
1325
- };
1302
+ if ('Request' in WINDOW$3 && isInstanceOf(fetchArgs[0], Request)) {
1303
+ return fetchArgs[0].url;
1326
1304
  }
1327
-
1328
- const arg = fetchArgs[0];
1329
- return {
1330
- url: getUrlFromResource(arg ),
1331
- method: hasProp(arg, 'method') ? String(arg.method).toUpperCase() : 'GET',
1332
- };
1305
+ return String(fetchArgs[0]);
1333
1306
  }
1307
+ /* eslint-enable @typescript-eslint/no-unsafe-member-access */
1334
1308
 
1335
1309
  /** JSDoc */
1336
1310
  function instrumentXHR() {
@@ -1343,11 +1317,10 @@ function instrumentXHR() {
1343
1317
  fill(xhrproto, 'open', function (originalOpen) {
1344
1318
  return function ( ...args) {
1345
1319
  const url = args[1];
1346
- const xhrInfo = (this[SENTRY_XHR_DATA_KEY] = {
1320
+ const xhrInfo = (this.__sentry_xhr__ = {
1347
1321
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
1348
1322
  method: isString$2(args[0]) ? args[0].toUpperCase() : args[0],
1349
1323
  url: args[1],
1350
- request_headers: {},
1351
1324
  });
1352
1325
 
1353
1326
  // if Sentry key appears in URL, don't capture it as a request
@@ -1358,7 +1331,7 @@ function instrumentXHR() {
1358
1331
 
1359
1332
  const onreadystatechangeHandler = () => {
1360
1333
  // For whatever reason, this is not the same instance here as from the outer method
1361
- const xhrInfo = this[SENTRY_XHR_DATA_KEY];
1334
+ const xhrInfo = this.__sentry_xhr__;
1362
1335
 
1363
1336
  if (!xhrInfo) {
1364
1337
  return;
@@ -1393,32 +1366,14 @@ function instrumentXHR() {
1393
1366
  this.addEventListener('readystatechange', onreadystatechangeHandler);
1394
1367
  }
1395
1368
 
1396
- // Intercepting `setRequestHeader` to access the request headers of XHR instance.
1397
- // This will only work for user/library defined headers, not for the default/browser-assigned headers.
1398
- // Request cookies are also unavailable for XHR, as `Cookie` header can't be defined by `setRequestHeader`.
1399
- fill(this, 'setRequestHeader', function (original) {
1400
- return function ( ...setRequestHeaderArgs) {
1401
- const [header, value] = setRequestHeaderArgs ;
1402
-
1403
- const xhrInfo = this[SENTRY_XHR_DATA_KEY];
1404
-
1405
- if (xhrInfo) {
1406
- xhrInfo.request_headers[header.toLowerCase()] = value;
1407
- }
1408
-
1409
- return original.apply(this, setRequestHeaderArgs);
1410
- };
1411
- });
1412
-
1413
1369
  return originalOpen.apply(this, args);
1414
1370
  };
1415
1371
  });
1416
1372
 
1417
1373
  fill(xhrproto, 'send', function (originalSend) {
1418
1374
  return function ( ...args) {
1419
- const sentryXhrData = this[SENTRY_XHR_DATA_KEY];
1420
- if (sentryXhrData && args[0] !== undefined) {
1421
- sentryXhrData.body = args[0];
1375
+ if (this.__sentry_xhr__ && args[0] !== undefined) {
1376
+ this.__sentry_xhr__.body = args[0];
1422
1377
  }
1423
1378
 
1424
1379
  triggerHandlers('xhr', {
@@ -1717,15 +1672,13 @@ function instrumentError() {
1717
1672
  url,
1718
1673
  });
1719
1674
 
1720
- if (_oldOnErrorHandler && !_oldOnErrorHandler.__SENTRY_LOADER__) {
1675
+ if (_oldOnErrorHandler) {
1721
1676
  // eslint-disable-next-line prefer-rest-params
1722
1677
  return _oldOnErrorHandler.apply(this, arguments);
1723
1678
  }
1724
1679
 
1725
1680
  return false;
1726
1681
  };
1727
-
1728
- WINDOW$3.onerror.__SENTRY_INSTRUMENTED__ = true;
1729
1682
  }
1730
1683
 
1731
1684
  let _oldOnUnhandledRejectionHandler = null;
@@ -1736,15 +1689,13 @@ function instrumentUnhandledRejection() {
1736
1689
  WINDOW$3.onunhandledrejection = function (e) {
1737
1690
  triggerHandlers('unhandledrejection', e);
1738
1691
 
1739
- if (_oldOnUnhandledRejectionHandler && !_oldOnUnhandledRejectionHandler.__SENTRY_LOADER__) {
1692
+ if (_oldOnUnhandledRejectionHandler) {
1740
1693
  // eslint-disable-next-line prefer-rest-params
1741
1694
  return _oldOnUnhandledRejectionHandler.apply(this, arguments);
1742
1695
  }
1743
1696
 
1744
1697
  return true;
1745
1698
  };
1746
-
1747
- WINDOW$3.onunhandledrejection.__SENTRY_INSTRUMENTED__ = true;
1748
1699
  }
1749
1700
 
1750
1701
  /* eslint-disable @typescript-eslint/no-unsafe-member-access */
@@ -2042,7 +1993,7 @@ function loadModule(moduleName) {
2042
1993
  * @returns A normalized version of the object, or `"**non-serializable**"` if any errors are thrown during normalization.
2043
1994
  */
2044
1995
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
2045
- function normalize(input, depth = 100, maxProperties = +Infinity) {
1996
+ function normalize(input, depth = +Infinity, maxProperties = +Infinity) {
2046
1997
  try {
2047
1998
  // since we're at the outermost level, we don't provide a key
2048
1999
  return visit('', input, depth, maxProperties);
@@ -2088,10 +2039,7 @@ function visit(
2088
2039
  const [memoize, unmemoize] = memo;
2089
2040
 
2090
2041
  // Get the simple cases out of the way first
2091
- if (
2092
- value == null || // this matches null and undefined -> eqeq not eqeqeq
2093
- (['number', 'boolean', 'string'].includes(typeof value) && !isNaN$1(value))
2094
- ) {
2042
+ if (value === null || (['number', 'boolean', 'string'].includes(typeof value) && !isNaN$1(value))) {
2095
2043
  return value ;
2096
2044
  }
2097
2045
 
@@ -2112,16 +2060,17 @@ function visit(
2112
2060
  return value ;
2113
2061
  }
2114
2062
 
2115
- // We can set `__sentry_override_normalization_depth__` on an object to ensure that from there
2116
- // We keep a certain amount of depth.
2117
- // This should be used sparingly, e.g. we use it for the redux integration to ensure we get a certain amount of state.
2118
- const remainingDepth =
2119
- typeof (value )['__sentry_override_normalization_depth__'] === 'number'
2120
- ? ((value )['__sentry_override_normalization_depth__'] )
2121
- : depth;
2063
+ // Do not normalize objects that we know have already been normalized. As a general rule, the
2064
+ // "__sentry_skip_normalization__" property should only be used sparingly and only should only be set on objects that
2065
+ // have already been normalized.
2066
+ let overriddenDepth = depth;
2067
+
2068
+ if (typeof (value )['__sentry_override_normalization_depth__'] === 'number') {
2069
+ overriddenDepth = (value )['__sentry_override_normalization_depth__'] ;
2070
+ }
2122
2071
 
2123
2072
  // We're also done if we've reached the max depth
2124
- if (remainingDepth === 0) {
2073
+ if (overriddenDepth === 0) {
2125
2074
  // At this point we know `serialized` is a string of the form `"[object XXXX]"`. Clean it up so it's just `"[XXXX]"`.
2126
2075
  return stringified.replace('object ', '');
2127
2076
  }
@@ -2137,7 +2086,7 @@ function visit(
2137
2086
  try {
2138
2087
  const jsonValue = valueWithToJSON.toJSON();
2139
2088
  // We need to normalize the return value of `.toJSON()` in case it has circular references
2140
- return visit('', jsonValue, remainingDepth - 1, maxProperties, memo);
2089
+ return visit('', jsonValue, overriddenDepth - 1, maxProperties, memo);
2141
2090
  } catch (err) {
2142
2091
  // pass (The built-in `toJSON` failed, but we can still try to do it ourselves)
2143
2092
  }
@@ -2166,7 +2115,7 @@ function visit(
2166
2115
 
2167
2116
  // Recursively visit all the child nodes
2168
2117
  const visitValue = visitable[visitKey];
2169
- normalized[visitKey] = visit(visitKey, visitValue, remainingDepth - 1, maxProperties, memo);
2118
+ normalized[visitKey] = visit(visitKey, visitValue, overriddenDepth - 1, maxProperties, memo);
2170
2119
 
2171
2120
  numAdded++;
2172
2121
  }
@@ -2178,7 +2127,6 @@ function visit(
2178
2127
  return normalized;
2179
2128
  }
2180
2129
 
2181
- /* eslint-disable complexity */
2182
2130
  /**
2183
2131
  * Stringify the given value. Handles various known special values and types.
2184
2132
  *
@@ -2229,6 +2177,11 @@ function stringifyValue(
2229
2177
  return '[NaN]';
2230
2178
  }
2231
2179
 
2180
+ // this catches `undefined` (but not `null`, which is a primitive and can be serialized on its own)
2181
+ if (value === void 0) {
2182
+ return '[undefined]';
2183
+ }
2184
+
2232
2185
  if (typeof value === 'function') {
2233
2186
  return `[Function: ${getFunctionName(value)}]`;
2234
2187
  }
@@ -2246,19 +2199,11 @@ function stringifyValue(
2246
2199
  // them to strings means that instances of classes which haven't defined their `toStringTag` will just come out as
2247
2200
  // `"[object Object]"`. If we instead look at the constructor's name (which is the same as the name of the class),
2248
2201
  // we can make sure that only plain objects come out that way.
2249
- const objName = getConstructorName(value);
2250
-
2251
- // Handle HTML Elements
2252
- if (/^HTML(\w*)Element$/.test(objName)) {
2253
- return `[HTMLElement: ${objName}]`;
2254
- }
2255
-
2256
- return `[object ${objName}]`;
2202
+ return `[object ${getConstructorName(value)}]`;
2257
2203
  } catch (err) {
2258
2204
  return `**non-serializable** (${err})`;
2259
2205
  }
2260
2206
  }
2261
- /* eslint-enable complexity */
2262
2207
 
2263
2208
  function getConstructorName(value) {
2264
2209
  const prototype = Object.getPrototypeOf(value);
@@ -2569,7 +2514,9 @@ function makePromiseBuffer(limit) {
2569
2514
  * // environments where DOM might not be available
2570
2515
  * @returns parsed URL object
2571
2516
  */
2572
- function parseUrl(url) {
2517
+ function parseUrl(url)
2518
+
2519
+ {
2573
2520
  if (!url) {
2574
2521
  return {};
2575
2522
  }
@@ -2587,8 +2534,6 @@ function parseUrl(url) {
2587
2534
  host: match[4],
2588
2535
  path: match[5],
2589
2536
  protocol: match[2],
2590
- search: query,
2591
- hash: fragment,
2592
2537
  relative: match[5] + query + fragment, // everything minus origin
2593
2538
  };
2594
2539
  }
@@ -2973,7 +2918,6 @@ const ITEM_TYPE_TO_DATA_CATEGORY_MAP = {
2973
2918
  profile: 'profile',
2974
2919
  replay_event: 'replay',
2975
2920
  replay_recording: 'replay',
2976
- check_in: 'monitor',
2977
2921
  };
2978
2922
 
2979
2923
  /**
@@ -3003,14 +2947,16 @@ function createEventEnvelopeHeaders(
3003
2947
  dsn,
3004
2948
  ) {
3005
2949
  const dynamicSamplingContext = event.sdkProcessingMetadata && event.sdkProcessingMetadata.dynamicSamplingContext;
2950
+
3006
2951
  return {
3007
2952
  event_id: event.event_id ,
3008
2953
  sent_at: new Date().toISOString(),
3009
2954
  ...(sdkInfo && { sdk: sdkInfo }),
3010
2955
  ...(!!tunnel && { dsn: dsnToString(dsn) }),
3011
- ...(dynamicSamplingContext && {
3012
- trace: dropUndefinedKeys({ ...dynamicSamplingContext }),
3013
- }),
2956
+ ...(event.type === 'transaction' &&
2957
+ dynamicSamplingContext && {
2958
+ trace: dropUndefinedKeys({ ...dynamicSamplingContext }),
2959
+ }),
3014
2960
  };
3015
2961
  }
3016
2962
 
@@ -3715,16 +3661,9 @@ class Scope {
3715
3661
  // errors with transaction and it relies on that.
3716
3662
  if (this._span) {
3717
3663
  event.contexts = { trace: this._span.getTraceContext(), ...event.contexts };
3718
- const transaction = this._span.transaction;
3719
- if (transaction) {
3720
- event.sdkProcessingMetadata = {
3721
- dynamicSamplingContext: transaction.getDynamicSamplingContext(),
3722
- ...event.sdkProcessingMetadata,
3723
- };
3724
- const transactionName = transaction.name;
3725
- if (transactionName) {
3726
- event.tags = { transaction: transactionName, ...event.tags };
3727
- }
3664
+ const transactionName = this._span.transaction && this._span.transaction.name;
3665
+ if (transactionName) {
3666
+ event.tags = { transaction: transactionName, ...event.tags };
3728
3667
  }
3729
3668
  }
3730
3669
 
@@ -3848,6 +3787,11 @@ const API_VERSION = 4;
3848
3787
  */
3849
3788
  const DEFAULT_BREADCRUMBS = 100;
3850
3789
 
3790
+ /**
3791
+ * A layer in the process stack.
3792
+ * @hidden
3793
+ */
3794
+
3851
3795
  /**
3852
3796
  * @inheritDoc
3853
3797
  */
@@ -4125,17 +4069,7 @@ class Hub {
4125
4069
  * @inheritDoc
4126
4070
  */
4127
4071
  startTransaction(context, customSamplingContext) {
4128
- const result = this._callExtensionMethod('startTransaction', context, customSamplingContext);
4129
-
4130
- if ((typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && !result) {
4131
- // eslint-disable-next-line no-console
4132
- console.warn(`Tracing extension 'startTransaction' has not been added. Call 'addTracingExtensions' before calling 'init':
4133
- Sentry.addTracingExtensions();
4134
- Sentry.init({...});
4135
- `);
4136
- }
4137
-
4138
- return result;
4072
+ return this._callExtensionMethod('startTransaction', context, customSamplingContext);
4139
4073
  }
4140
4074
 
4141
4075
  /**
@@ -4220,10 +4154,13 @@ Sentry.init({...});
4220
4154
  */
4221
4155
  _sendSessionUpdate() {
4222
4156
  const { scope, client } = this.getStackTop();
4157
+ if (!scope) return;
4223
4158
 
4224
4159
  const session = scope.getSession();
4225
- if (session && client && client.captureSession) {
4226
- client.captureSession(session);
4160
+ if (session) {
4161
+ if (client && client.captureSession) {
4162
+ client.captureSession(session);
4163
+ }
4227
4164
  }
4228
4165
  }
4229
4166
 
@@ -4293,28 +4230,47 @@ function getCurrentHub() {
4293
4230
  // Get main carrier (global for every environment)
4294
4231
  const registry = getMainCarrier();
4295
4232
 
4296
- if (registry.__SENTRY__ && registry.__SENTRY__.acs) {
4297
- const hub = registry.__SENTRY__.acs.getCurrentHub();
4298
-
4299
- if (hub) {
4300
- return hub;
4301
- }
4302
- }
4303
-
4304
- // Return hub that lives on a global object
4305
- return getGlobalHub(registry);
4306
- }
4307
-
4308
- function getGlobalHub(registry = getMainCarrier()) {
4309
4233
  // If there's no hub, or its an old API, assign a new one
4310
4234
  if (!hasHubOnCarrier(registry) || getHubFromCarrier(registry).isOlderThan(API_VERSION)) {
4311
4235
  setHubOnCarrier(registry, new Hub());
4312
4236
  }
4313
4237
 
4238
+ // Prefer domains over global if they are there (applicable only to Node environment)
4239
+ if (isNodeEnv()) {
4240
+ return getHubFromActiveDomain(registry);
4241
+ }
4314
4242
  // Return hub that lives on a global object
4315
4243
  return getHubFromCarrier(registry);
4316
4244
  }
4317
4245
 
4246
+ /**
4247
+ * Try to read the hub from an active domain, and fallback to the registry if one doesn't exist
4248
+ * @returns discovered hub
4249
+ */
4250
+ function getHubFromActiveDomain(registry) {
4251
+ try {
4252
+ const sentry = getMainCarrier().__SENTRY__;
4253
+ const activeDomain = sentry && sentry.extensions && sentry.extensions.domain && sentry.extensions.domain.active;
4254
+
4255
+ // If there's no active domain, just return global hub
4256
+ if (!activeDomain) {
4257
+ return getHubFromCarrier(registry);
4258
+ }
4259
+
4260
+ // If there's no hub on current domain, or it's an old API, assign a new one
4261
+ if (!hasHubOnCarrier(activeDomain) || getHubFromCarrier(activeDomain).isOlderThan(API_VERSION)) {
4262
+ const registryHubTopStack = getHubFromCarrier(registry).getStackTop();
4263
+ setHubOnCarrier(activeDomain, new Hub(registryHubTopStack.client, Scope.clone(registryHubTopStack.scope)));
4264
+ }
4265
+
4266
+ // Return hub that lives on a domain
4267
+ return getHubFromCarrier(activeDomain);
4268
+ } catch (_Oo) {
4269
+ // Return hub that lives on a global object
4270
+ return getHubFromCarrier(registry);
4271
+ }
4272
+ }
4273
+
4318
4274
  /**
4319
4275
  * This will tell whether a carrier has a hub on it or not
4320
4276
  * @param carrier object
@@ -4388,69 +4344,6 @@ var SpanStatus; (function (SpanStatus) {
4388
4344
  const DataLoss = 'data_loss'; SpanStatus["DataLoss"] = DataLoss;
4389
4345
  })(SpanStatus || (SpanStatus = {}));
4390
4346
 
4391
- /**
4392
- * Wraps a function with a transaction/span and finishes the span after the function is done.
4393
- *
4394
- * Note that if you have not enabled tracing extensions via `addTracingExtensions`, this function
4395
- * will not generate spans, and the `span` returned from the callback may be undefined.
4396
- *
4397
- * This function is meant to be used internally and may break at any time. Use at your own risk.
4398
- *
4399
- * @internal
4400
- * @private
4401
- */
4402
- function trace(
4403
- context,
4404
- callback,
4405
- // eslint-disable-next-line @typescript-eslint/no-empty-function
4406
- onError = () => {},
4407
- ) {
4408
- const ctx = { ...context };
4409
- // If a name is set and a description is not, set the description to the name.
4410
- if (ctx.name !== undefined && ctx.description === undefined) {
4411
- ctx.description = ctx.name;
4412
- }
4413
-
4414
- const hub = getCurrentHub();
4415
- const scope = hub.getScope();
4416
-
4417
- const parentSpan = scope.getSpan();
4418
- const activeSpan = parentSpan ? parentSpan.startChild(ctx) : hub.startTransaction(ctx);
4419
- scope.setSpan(activeSpan);
4420
-
4421
- function finishAndSetSpan() {
4422
- activeSpan && activeSpan.finish();
4423
- hub.getScope().setSpan(parentSpan);
4424
- }
4425
-
4426
- let maybePromiseResult;
4427
- try {
4428
- maybePromiseResult = callback(activeSpan);
4429
- } catch (e) {
4430
- activeSpan && activeSpan.setStatus('internal_error');
4431
- onError(e);
4432
- finishAndSetSpan();
4433
- throw e;
4434
- }
4435
-
4436
- if (isThenable(maybePromiseResult)) {
4437
- Promise.resolve(maybePromiseResult).then(
4438
- () => {
4439
- finishAndSetSpan();
4440
- },
4441
- e => {
4442
- activeSpan && activeSpan.setStatus('internal_error');
4443
- onError(e);
4444
- finishAndSetSpan();
4445
- },
4446
- );
4447
- } else {
4448
- finishAndSetSpan();
4449
- }
4450
-
4451
- return maybePromiseResult;
4452
- }
4453
-
4454
4347
  // Note: All functions in this file are typed with a return value of `ReturnType<Hub[HUB_FUNCTION]>`,
4455
4348
  // where HUB_FUNCTION is some method on the Hub class.
4456
4349
  //
@@ -4792,11 +4685,7 @@ function prepareEvent(
4792
4685
 
4793
4686
  applyClientOptions(prepared, options);
4794
4687
  applyIntegrationsMetadata(prepared, integrations);
4795
-
4796
- // Only apply debug metadata to error events.
4797
- if (event.type === undefined) {
4798
- applyDebugMetadata(prepared, options.stackParser);
4799
- }
4688
+ applyDebugMetadata(prepared, options.stackParser);
4800
4689
 
4801
4690
  // If we have scope given to us, use it as the base for further modifications.
4802
4691
  // This allows us to prevent unnecessary copying of data if `captureContext` is not provided.
@@ -4873,8 +4762,6 @@ function applyClientOptions(event, options) {
4873
4762
  }
4874
4763
  }
4875
4764
 
4876
- const debugIdStackParserCache = new WeakMap();
4877
-
4878
4765
  /**
4879
4766
  * Applies debug metadata images to the event in order to apply source maps by looking up their debug ID.
4880
4767
  */
@@ -4885,28 +4772,10 @@ function applyDebugMetadata(event, stackParser) {
4885
4772
  return;
4886
4773
  }
4887
4774
 
4888
- let debugIdStackFramesCache;
4889
- const cachedDebugIdStackFrameCache = debugIdStackParserCache.get(stackParser);
4890
- if (cachedDebugIdStackFrameCache) {
4891
- debugIdStackFramesCache = cachedDebugIdStackFrameCache;
4892
- } else {
4893
- debugIdStackFramesCache = new Map();
4894
- debugIdStackParserCache.set(stackParser, debugIdStackFramesCache);
4895
- }
4896
-
4897
4775
  // Build a map of filename -> debug_id
4898
4776
  const filenameDebugIdMap = Object.keys(debugIdMap).reduce((acc, debugIdStackTrace) => {
4899
- let parsedStack;
4900
- const cachedParsedStack = debugIdStackFramesCache.get(debugIdStackTrace);
4901
- if (cachedParsedStack) {
4902
- parsedStack = cachedParsedStack;
4903
- } else {
4904
- parsedStack = stackParser(debugIdStackTrace);
4905
- debugIdStackFramesCache.set(debugIdStackTrace, parsedStack);
4906
- }
4907
-
4908
- for (let i = parsedStack.length - 1; i >= 0; i--) {
4909
- const stackFrame = parsedStack[i];
4777
+ const parsedStack = stackParser(debugIdStackTrace);
4778
+ for (const stackFrame of parsedStack) {
4910
4779
  if (stackFrame.filename) {
4911
4780
  acc[stackFrame.filename] = debugIdMap[debugIdStackTrace];
4912
4781
  break;
@@ -5083,20 +4952,16 @@ class BaseClient {
5083
4952
  */
5084
4953
  constructor(options) {BaseClient.prototype.__init.call(this);BaseClient.prototype.__init2.call(this);BaseClient.prototype.__init3.call(this);BaseClient.prototype.__init4.call(this);BaseClient.prototype.__init5.call(this);
5085
4954
  this._options = options;
5086
-
5087
4955
  if (options.dsn) {
5088
4956
  this._dsn = makeDsn(options.dsn);
5089
- } else {
5090
- (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.warn('No DSN provided, client will not do anything.');
5091
- }
5092
-
5093
- if (this._dsn) {
5094
4957
  const url = getEnvelopeEndpointWithUrlEncodedAuth(this._dsn, options);
5095
4958
  this._transport = options.transport({
5096
4959
  recordDroppedEvent: this.recordDroppedEvent.bind(this),
5097
4960
  ...options.transportOptions,
5098
4961
  url,
5099
4962
  });
4963
+ } else {
4964
+ (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.warn('No DSN provided, client will not do anything.');
5100
4965
  }
5101
4966
  }
5102
4967
 
@@ -5815,7 +5680,7 @@ function getEventForEnvelopeItem(item, type) {
5815
5680
  return Array.isArray(item) ? (item )[1] : undefined;
5816
5681
  }
5817
5682
 
5818
- const SDK_VERSION = '7.53.0';
5683
+ const SDK_VERSION = '7.46.0';
5819
5684
 
5820
5685
  let originalFunctionToString;
5821
5686
 
@@ -5838,17 +5703,11 @@ class FunctionToString {constructor() { FunctionToString.prototype.__init.call(
5838
5703
  // eslint-disable-next-line @typescript-eslint/unbound-method
5839
5704
  originalFunctionToString = Function.prototype.toString;
5840
5705
 
5841
- // intrinsics (like Function.prototype) might be immutable in some environments
5842
- // e.g. Node with --frozen-intrinsics, XS (an embedded JavaScript engine) or SES (a JavaScript proposal)
5843
- try {
5844
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
5845
- Function.prototype.toString = function ( ...args) {
5846
- const context = getOriginalFunction(this) || this;
5847
- return originalFunctionToString.apply(context, args);
5848
- };
5849
- } catch (e) {
5850
- // ignore errors here, just don't patch this
5851
- }
5706
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
5707
+ Function.prototype.toString = function ( ...args) {
5708
+ const context = getOriginalFunction(this) || this;
5709
+ return originalFunctionToString.apply(context, args);
5710
+ };
5852
5711
  }
5853
5712
  } FunctionToString.__initStatic();
5854
5713
 
@@ -5996,9 +5855,8 @@ function _getPossibleEventMessages(event) {
5996
5855
  return [event.message];
5997
5856
  }
5998
5857
  if (event.exception) {
5999
- const { values } = event.exception;
6000
5858
  try {
6001
- const { type = '', value = '' } = (values && values[values.length - 1]) || {};
5859
+ const { type = '', value = '' } = (event.exception.values && event.exception.values[0]) || {};
6002
5860
  return [`${value}`, `${type}: ${value}`];
6003
5861
  } catch (oO) {
6004
5862
  (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.error(`Cannot extract message for event ${getEventDescription(event)}`);
@@ -6048,9 +5906,9 @@ function _getEventFilterUrl(event) {
6048
5906
  }
6049
5907
 
6050
5908
  var Integrations = /*#__PURE__*/Object.freeze({
6051
- __proto__: null,
6052
- FunctionToString: FunctionToString,
6053
- InboundFilters: InboundFilters
5909
+ __proto__: null,
5910
+ FunctionToString: FunctionToString,
5911
+ InboundFilters: InboundFilters
6054
5912
  });
6055
5913
 
6056
5914
  const WINDOW$1 = GLOBAL_OBJ ;
@@ -6657,14 +6515,12 @@ function _consoleBreadcrumb(handlerData) {
6657
6515
  function _xhrBreadcrumb(handlerData) {
6658
6516
  const { startTimestamp, endTimestamp } = handlerData;
6659
6517
 
6660
- const sentryXhrData = handlerData.xhr[SENTRY_XHR_DATA_KEY];
6661
-
6662
6518
  // We only capture complete, non-sentry requests
6663
- if (!startTimestamp || !endTimestamp || !sentryXhrData) {
6519
+ if (!startTimestamp || !endTimestamp || !handlerData.xhr.__sentry_xhr__) {
6664
6520
  return;
6665
6521
  }
6666
6522
 
6667
- const { method, url, status_code, body } = sentryXhrData;
6523
+ const { method, url, status_code, body } = handlerData.xhr.__sentry_xhr__;
6668
6524
 
6669
6525
  const data = {
6670
6526
  method,
@@ -6782,43 +6638,6 @@ function _isEvent(event) {
6782
6638
  return event && !!(event ).target;
6783
6639
  }
6784
6640
 
6785
- /**
6786
- * Creates an envelope from a user feedback.
6787
- */
6788
- function createUserFeedbackEnvelope(
6789
- feedback,
6790
- {
6791
- metadata,
6792
- tunnel,
6793
- dsn,
6794
- }
6795
-
6796
- ,
6797
- ) {
6798
- const headers = {
6799
- event_id: feedback.event_id,
6800
- sent_at: new Date().toISOString(),
6801
- ...(metadata &&
6802
- metadata.sdk && {
6803
- sdk: {
6804
- name: metadata.sdk.name,
6805
- version: metadata.sdk.version,
6806
- },
6807
- }),
6808
- ...(!!tunnel && !!dsn && { dsn: dsnToString(dsn) }),
6809
- };
6810
- const item = createUserFeedbackEnvelopeItem(feedback);
6811
-
6812
- return createEnvelope(headers, [item]);
6813
- }
6814
-
6815
- function createUserFeedbackEnvelopeItem(feedback) {
6816
- const feedbackHeaders = {
6817
- type: 'user_report',
6818
- };
6819
- return [feedbackHeaders, feedback];
6820
- }
6821
-
6822
6641
  /**
6823
6642
  * Configuration options for the Sentry Browser SDK.
6824
6643
  * @see @sentry/types Options for more information.
@@ -6901,23 +6720,6 @@ class BrowserClient extends BaseClient {
6901
6720
  super.sendEvent(event, hint);
6902
6721
  }
6903
6722
 
6904
- /**
6905
- * Sends user feedback to Sentry.
6906
- */
6907
- captureUserFeedback(feedback) {
6908
- if (!this._isEnabled()) {
6909
- (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.warn('SDK not enabled, will not capture user feedback.');
6910
- return;
6911
- }
6912
-
6913
- const envelope = createUserFeedbackEnvelope(feedback, {
6914
- metadata: this.getSdkMetadata(),
6915
- dsn: this.getDsn(),
6916
- tunnel: this.getOptions().tunnel,
6917
- });
6918
- void this._sendEnvelope(envelope);
6919
- }
6920
-
6921
6723
  /**
6922
6724
  * @inheritDoc
6923
6725
  */
@@ -7160,7 +6962,7 @@ function createFrame(filename, func, lineno, colno) {
7160
6962
 
7161
6963
  // Chromium based browsers: Chrome, Brave, new Opera, new Edge
7162
6964
  const chromeRegex =
7163
- /^\s*at (?:(.+?\)(?: \[.+\])?|.*?) ?\((?:address at )?)?(?:async )?((?:<anonymous>|[-a-z]+:|.*bundle|\/)?.*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i;
6965
+ /^\s*at (?:(.*\).*?|.*?) ?\((?:address at )?)?(?:async )?((?:file|https?|blob|chrome-extension|address|native|eval|webpack|<anonymous>|[-a-z]+:|.*bundle|\/)?.*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i;
7164
6966
  const chromeEvalRegex = /\((\S*)(?::(\d+))(?::(\d+))\)/;
7165
6967
 
7166
6968
  const chrome = line => {
@@ -7196,7 +6998,7 @@ const chromeStackLineParser = [CHROME_PRIORITY, chrome];
7196
6998
  // generates filenames without a prefix like `file://` the filenames in the stacktrace are just 42.js
7197
6999
  // We need this specific case for now because we want no other regex to match.
7198
7000
  const geckoREgex =
7199
- /^\s*(.*?)(?:\((.*?)\))?(?:^|@)?((?:[-a-z]+)?:\/.*?|\[native code\]|[^@]*(?:bundle|\d+\.js)|\/[\w\-. /=]+)(?::(\d+))?(?::(\d+))?\s*$/i;
7001
+ /^\s*(.*?)(?:\((.*?)\))?(?:^|@)?((?:file|https?|blob|chrome|webpack|resource|moz-extension|safari-extension|safari-web-extension|capacitor)?:\/.*?|\[native code\]|[^@]*(?:bundle|\d+\.js)|\/[\w\-. /=]+)(?::(\d+))?(?::(\d+))?\s*$/i;
7200
7002
  const geckoEvalRegex = /(\S+) line (\d+)(?: > eval line \d+)* > eval/i;
7201
7003
 
7202
7004
  const gecko = line => {
@@ -7228,7 +7030,8 @@ const gecko = line => {
7228
7030
 
7229
7031
  const geckoStackLineParser = [GECKO_PRIORITY, gecko];
7230
7032
 
7231
- const winjsRegex = /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:[-a-z]+):.*?):(\d+)(?::(\d+))?\)?\s*$/i;
7033
+ const winjsRegex =
7034
+ /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:file|ms-appx|https?|webpack|blob):.*?):(\d+)(?::(\d+))?\)?\s*$/i;
7232
7035
 
7233
7036
  const winjs = line => {
7234
7037
  const parts = winjsRegex.exec(line);
@@ -8274,13 +8077,13 @@ function startSessionTracking() {
8274
8077
  }
8275
8078
 
8276
8079
  var index$1 = /*#__PURE__*/Object.freeze({
8277
- __proto__: null,
8278
- GlobalHandlers: GlobalHandlers,
8279
- TryCatch: TryCatch,
8280
- Breadcrumbs: Breadcrumbs,
8281
- LinkedErrors: LinkedErrors,
8282
- HttpContext: HttpContext,
8283
- Dedupe: Dedupe
8080
+ __proto__: null,
8081
+ GlobalHandlers: GlobalHandlers,
8082
+ TryCatch: TryCatch,
8083
+ Breadcrumbs: Breadcrumbs,
8084
+ LinkedErrors: LinkedErrors,
8085
+ HttpContext: HttpContext,
8086
+ Dedupe: Dedupe
8284
8087
  });
8285
8088
 
8286
8089
  // exporting a separate copy of `WINDOW` rather than exporting the one from `@sentry/browser`
@@ -8293,14 +8096,11 @@ const REPLAY_SESSION_KEY = 'sentryReplaySession';
8293
8096
  const REPLAY_EVENT_NAME = 'replay_event';
8294
8097
  const UNABLE_TO_SEND_REPLAY = 'Unable to send Replay';
8295
8098
 
8296
- // The idle limit for a session after which recording is paused.
8297
- const SESSION_IDLE_PAUSE_DURATION = 300000; // 5 minutes in ms
8298
-
8299
- // The idle limit for a session after which the session expires.
8300
- const SESSION_IDLE_EXPIRE_DURATION = 900000; // 15 minutes in ms
8099
+ // The idle limit for a session
8100
+ const SESSION_IDLE_DURATION = 300000; // 5 minutes in ms
8301
8101
 
8302
8102
  // The maximum length of a session
8303
- const MAX_SESSION_LIFE = 3600000; // 60 minutes in ms
8103
+ const MAX_SESSION_LIFE = 3600000; // 60 minutes
8304
8104
 
8305
8105
  /** Default flush delays */
8306
8106
  const DEFAULT_FLUSH_MIN_DELAY = 5000;
@@ -8309,16 +8109,13 @@ const DEFAULT_FLUSH_MIN_DELAY = 5000;
8309
8109
  const DEFAULT_FLUSH_MAX_DELAY = 5500;
8310
8110
 
8311
8111
  /* How long to wait for error checkouts */
8312
- const BUFFER_CHECKOUT_TIME = 60000;
8112
+ const ERROR_CHECKOUT_TIME = 60000;
8313
8113
 
8314
8114
  const RETRY_BASE_INTERVAL = 5000;
8315
8115
  const RETRY_MAX_COUNT = 3;
8316
8116
 
8317
- /* The max (uncompressed) size in bytes of a network body. Any body larger than this will be truncated. */
8318
- const NETWORK_BODY_MAX_SIZE = 150000;
8319
-
8320
- /* The max size of a single console arg that is captured. Any arg larger than this will be truncated. */
8321
- const CONSOLE_ARG_MAX_SIZE = 5000;
8117
+ /* The max (uncompressed) size in bytes of a network body. Any body larger than this will be dropped. */
8118
+ const NETWORK_BODY_MAX_SIZE = 300000;
8322
8119
 
8323
8120
  var NodeType$1;
8324
8121
  (function (NodeType) {
@@ -8355,7 +8152,7 @@ function maskInputValue({ input, maskInputSelector, unmaskInputSelector, maskInp
8355
8152
  if (unmaskInputSelector && input.matches(unmaskInputSelector)) {
8356
8153
  return text;
8357
8154
  }
8358
- if (input.hasAttribute('data-rr-is-password')) {
8155
+ if (input.hasAttribute('rr_is_password')) {
8359
8156
  type = 'password';
8360
8157
  }
8361
8158
  if (isInputTypeMasked({ maskInputOptions, tagName, type }) ||
@@ -8388,21 +8185,6 @@ function is2DCanvasBlank(canvas) {
8388
8185
  }
8389
8186
  return true;
8390
8187
  }
8391
- function getInputType(element) {
8392
- const type = element.type;
8393
- return element.hasAttribute('data-rr-is-password')
8394
- ? 'password'
8395
- : type
8396
- ? type.toLowerCase()
8397
- : null;
8398
- }
8399
- function getInputValue(el, tagName, type) {
8400
- typeof type === 'string' ? type.toLowerCase() : '';
8401
- if (tagName === 'INPUT' && (type === 'radio' || type === 'checkbox')) {
8402
- return el.getAttribute('value') || '';
8403
- }
8404
- return el.value;
8405
- }
8406
8188
 
8407
8189
  let _id = 1;
8408
8190
  const tagNameRegex = new RegExp('[^a-z0-9-_:]');
@@ -8441,13 +8223,6 @@ function getCssRuleString(rule) {
8441
8223
  catch (_a) {
8442
8224
  }
8443
8225
  }
8444
- return validateStringifiedCssRule(cssStringified);
8445
- }
8446
- function validateStringifiedCssRule(cssStringified) {
8447
- if (cssStringified.indexOf(':') > -1) {
8448
- const regex = /(\[(?:[\w-]+)[^\\])(:(?:[\w-]+)\])/gm;
8449
- return cssStringified.replace(regex, '$1\\$2');
8450
- }
8451
8226
  return cssStringified;
8452
8227
  }
8453
8228
  function isCSSImportRule(rule) {
@@ -8456,7 +8231,7 @@ function isCSSImportRule(rule) {
8456
8231
  function stringifyStyleSheet(sheet) {
8457
8232
  return sheet.cssRules
8458
8233
  ? Array.from(sheet.cssRules)
8459
- .map((rule) => rule.cssText ? validateStringifiedCssRule(rule.cssText) : '')
8234
+ .map((rule) => rule.cssText || '')
8460
8235
  .join('')
8461
8236
  : '';
8462
8237
  }
@@ -8791,15 +8566,14 @@ function serializeNode(n, options) {
8791
8566
  tagName === 'select' ||
8792
8567
  tagName === 'option') {
8793
8568
  const el = n;
8794
- const type = getInputType(el);
8795
- const value = getInputValue(el, tagName.toUpperCase(), type);
8569
+ const value = getInputValue(tagName, el, attributes);
8796
8570
  const checked = n.checked;
8797
- if (type !== 'submit' &&
8798
- type !== 'button' &&
8571
+ if (attributes.type !== 'submit' &&
8572
+ attributes.type !== 'button' &&
8799
8573
  value) {
8800
8574
  attributes.value = maskInputValue({
8801
8575
  input: el,
8802
- type,
8576
+ type: attributes.type,
8803
8577
  tagName,
8804
8578
  value,
8805
8579
  maskInputSelector,
@@ -9272,8 +9046,15 @@ function snapshot(n, options) {
9272
9046
  function skipAttribute(tagName, attributeName, value) {
9273
9047
  return ((tagName === 'video' || tagName === 'audio') && attributeName === 'autoplay');
9274
9048
  }
9049
+ function getInputValue(tagName, el, attributes) {
9050
+ if (tagName === 'input' &&
9051
+ (attributes.type === 'radio' || attributes.type === 'checkbox')) {
9052
+ return el.getAttribute('value') || '';
9053
+ }
9054
+ return el.value;
9055
+ }
9275
9056
 
9276
- var EventType$1;
9057
+ var EventType;
9277
9058
  (function (EventType) {
9278
9059
  EventType[EventType["DomContentLoaded"] = 0] = "DomContentLoaded";
9279
9060
  EventType[EventType["Load"] = 1] = "Load";
@@ -9282,7 +9063,7 @@ var EventType$1;
9282
9063
  EventType[EventType["Meta"] = 4] = "Meta";
9283
9064
  EventType[EventType["Custom"] = 5] = "Custom";
9284
9065
  EventType[EventType["Plugin"] = 6] = "Plugin";
9285
- })(EventType$1 || (EventType$1 = {}));
9066
+ })(EventType || (EventType = {}));
9286
9067
  var IncrementalSource;
9287
9068
  (function (IncrementalSource) {
9288
9069
  IncrementalSource[IncrementalSource["Mutation"] = 0] = "Mutation";
@@ -9897,9 +9678,9 @@ class MutationBuffer {
9897
9678
  this.attributes.push(item);
9898
9679
  }
9899
9680
  if (m.attributeName === 'type' &&
9900
- target.tagName === 'INPUT' &&
9681
+ m.target.tagName === 'INPUT' &&
9901
9682
  (m.oldValue || '').toLowerCase() === 'password') {
9902
- target.setAttribute('data-rr-is-password', 'true');
9683
+ m.target.setAttribute('rr_is_password', 'true');
9903
9684
  }
9904
9685
  if (m.attributeName === 'style') {
9905
9686
  const old = this.doc.createElement('span');
@@ -10296,25 +10077,27 @@ function initInputObserver({ inputCb, doc, mirror, blockClass, blockSelector, un
10296
10077
  isBlocked(target, blockClass, blockSelector, unblockSelector)) {
10297
10078
  return;
10298
10079
  }
10299
- const el = target;
10300
- const type = getInputType(el);
10301
- if (el.classList.contains(ignoreClass) ||
10302
- (ignoreSelector && el.matches(ignoreSelector))) {
10080
+ let type = target.type;
10081
+ if (target.classList.contains(ignoreClass) ||
10082
+ (ignoreSelector && target.matches(ignoreSelector))) {
10303
10083
  return;
10304
10084
  }
10305
- let text = getInputValue(el, tagName, type);
10085
+ let text = target.value;
10306
10086
  let isChecked = false;
10087
+ if (target.hasAttribute('rr_is_password')) {
10088
+ type = 'password';
10089
+ }
10307
10090
  if (type === 'radio' || type === 'checkbox') {
10308
10091
  isChecked = target.checked;
10309
10092
  }
10310
- if (hasInputMaskOptions({
10093
+ else if (hasInputMaskOptions({
10311
10094
  maskInputOptions,
10312
10095
  maskInputSelector,
10313
10096
  tagName,
10314
10097
  type,
10315
10098
  })) {
10316
10099
  text = maskInputValue({
10317
- input: el,
10100
+ input: target,
10318
10101
  maskInputOptions,
10319
10102
  maskInputSelector,
10320
10103
  unmaskInputSelector,
@@ -10331,18 +10114,8 @@ function initInputObserver({ inputCb, doc, mirror, blockClass, blockSelector, un
10331
10114
  .querySelectorAll(`input[type="radio"][name="${name}"]`)
10332
10115
  .forEach((el) => {
10333
10116
  if (el !== target) {
10334
- const text = maskInputValue({
10335
- input: el,
10336
- maskInputOptions,
10337
- maskInputSelector,
10338
- unmaskInputSelector,
10339
- tagName,
10340
- type,
10341
- value: getInputValue(el, tagName, type),
10342
- maskInputFn,
10343
- });
10344
10117
  cbWithDedup(el, callbackWrapper(wrapEventWithUserTriggeredFlag)({
10345
- text,
10118
+ text: el.value,
10346
10119
  isChecked: !isChecked,
10347
10120
  userTriggered: false,
10348
10121
  }, userTriggeredOnInput));
@@ -11255,17 +11028,17 @@ function record(options = {}) {
11255
11028
  wrappedEmit = (e, isCheckout) => {
11256
11029
  var _a;
11257
11030
  if (((_a = mutationBuffers[0]) === null || _a === void 0 ? void 0 : _a.isFrozen()) &&
11258
- e.type !== EventType$1.FullSnapshot &&
11259
- !(e.type === EventType$1.IncrementalSnapshot &&
11031
+ e.type !== EventType.FullSnapshot &&
11032
+ !(e.type === EventType.IncrementalSnapshot &&
11260
11033
  e.data.source === IncrementalSource.Mutation)) {
11261
11034
  mutationBuffers.forEach((buf) => buf.unfreeze());
11262
11035
  }
11263
11036
  emit(eventProcessor(e), isCheckout);
11264
- if (e.type === EventType$1.FullSnapshot) {
11037
+ if (e.type === EventType.FullSnapshot) {
11265
11038
  lastFullSnapshotEvent = e;
11266
11039
  incrementalSnapshotCount = 0;
11267
11040
  }
11268
- else if (e.type === EventType$1.IncrementalSnapshot) {
11041
+ else if (e.type === EventType.IncrementalSnapshot) {
11269
11042
  if (e.data.source === IncrementalSource.Mutation &&
11270
11043
  e.data.isAttachIframe) {
11271
11044
  return;
@@ -11281,16 +11054,16 @@ function record(options = {}) {
11281
11054
  };
11282
11055
  const wrappedMutationEmit = (m) => {
11283
11056
  wrappedEmit(wrapEvent({
11284
- type: EventType$1.IncrementalSnapshot,
11057
+ type: EventType.IncrementalSnapshot,
11285
11058
  data: Object.assign({ source: IncrementalSource.Mutation }, m),
11286
11059
  }));
11287
11060
  };
11288
11061
  const wrappedScrollEmit = (p) => wrappedEmit(wrapEvent({
11289
- type: EventType$1.IncrementalSnapshot,
11062
+ type: EventType.IncrementalSnapshot,
11290
11063
  data: Object.assign({ source: IncrementalSource.Scroll }, p),
11291
11064
  }));
11292
11065
  const wrappedCanvasMutationEmit = (p) => wrappedEmit(wrapEvent({
11293
- type: EventType$1.IncrementalSnapshot,
11066
+ type: EventType.IncrementalSnapshot,
11294
11067
  data: Object.assign({ source: IncrementalSource.CanvasMutation }, p),
11295
11068
  }));
11296
11069
  const iframeManager = new IframeManager({
@@ -11335,7 +11108,7 @@ function record(options = {}) {
11335
11108
  takeFullSnapshot = (isCheckout = false) => {
11336
11109
  var _a, _b, _c, _d;
11337
11110
  wrappedEmit(wrapEvent({
11338
- type: EventType$1.Meta,
11111
+ type: EventType.Meta,
11339
11112
  data: {
11340
11113
  href: window.location.href,
11341
11114
  width: getWindowWidth(),
@@ -11378,7 +11151,7 @@ function record(options = {}) {
11378
11151
  }
11379
11152
  mirror.map = idNodeMap;
11380
11153
  wrappedEmit(wrapEvent({
11381
- type: EventType$1.FullSnapshot,
11154
+ type: EventType.FullSnapshot,
11382
11155
  data: {
11383
11156
  node,
11384
11157
  initialOffset: {
@@ -11403,7 +11176,7 @@ function record(options = {}) {
11403
11176
  const handlers = [];
11404
11177
  handlers.push(on$1('DOMContentLoaded', () => {
11405
11178
  wrappedEmit(wrapEvent({
11406
- type: EventType$1.DomContentLoaded,
11179
+ type: EventType.DomContentLoaded,
11407
11180
  data: {},
11408
11181
  }));
11409
11182
  }));
@@ -11413,40 +11186,40 @@ function record(options = {}) {
11413
11186
  onMutation,
11414
11187
  mutationCb: wrappedMutationEmit,
11415
11188
  mousemoveCb: (positions, source) => wrappedEmit(wrapEvent({
11416
- type: EventType$1.IncrementalSnapshot,
11189
+ type: EventType.IncrementalSnapshot,
11417
11190
  data: {
11418
11191
  source,
11419
11192
  positions,
11420
11193
  },
11421
11194
  })),
11422
11195
  mouseInteractionCb: (d) => wrappedEmit(wrapEvent({
11423
- type: EventType$1.IncrementalSnapshot,
11196
+ type: EventType.IncrementalSnapshot,
11424
11197
  data: Object.assign({ source: IncrementalSource.MouseInteraction }, d),
11425
11198
  })),
11426
11199
  scrollCb: wrappedScrollEmit,
11427
11200
  viewportResizeCb: (d) => wrappedEmit(wrapEvent({
11428
- type: EventType$1.IncrementalSnapshot,
11201
+ type: EventType.IncrementalSnapshot,
11429
11202
  data: Object.assign({ source: IncrementalSource.ViewportResize }, d),
11430
11203
  })),
11431
11204
  inputCb: (v) => wrappedEmit(wrapEvent({
11432
- type: EventType$1.IncrementalSnapshot,
11205
+ type: EventType.IncrementalSnapshot,
11433
11206
  data: Object.assign({ source: IncrementalSource.Input }, v),
11434
11207
  })),
11435
11208
  mediaInteractionCb: (p) => wrappedEmit(wrapEvent({
11436
- type: EventType$1.IncrementalSnapshot,
11209
+ type: EventType.IncrementalSnapshot,
11437
11210
  data: Object.assign({ source: IncrementalSource.MediaInteraction }, p),
11438
11211
  })),
11439
11212
  styleSheetRuleCb: (r) => wrappedEmit(wrapEvent({
11440
- type: EventType$1.IncrementalSnapshot,
11213
+ type: EventType.IncrementalSnapshot,
11441
11214
  data: Object.assign({ source: IncrementalSource.StyleSheetRule }, r),
11442
11215
  })),
11443
11216
  styleDeclarationCb: (r) => wrappedEmit(wrapEvent({
11444
- type: EventType$1.IncrementalSnapshot,
11217
+ type: EventType.IncrementalSnapshot,
11445
11218
  data: Object.assign({ source: IncrementalSource.StyleDeclaration }, r),
11446
11219
  })),
11447
11220
  canvasMutationCb: wrappedCanvasMutationEmit,
11448
11221
  fontCb: (p) => wrappedEmit(wrapEvent({
11449
- type: EventType$1.IncrementalSnapshot,
11222
+ type: EventType.IncrementalSnapshot,
11450
11223
  data: Object.assign({ source: IncrementalSource.Font }, p),
11451
11224
  })),
11452
11225
  blockClass,
@@ -11479,7 +11252,7 @@ function record(options = {}) {
11479
11252
  observer: p.observer,
11480
11253
  options: p.options,
11481
11254
  callback: (payload) => wrappedEmit(wrapEvent({
11482
- type: EventType$1.Plugin,
11255
+ type: EventType.Plugin,
11483
11256
  data: {
11484
11257
  plugin: p.name,
11485
11258
  payload,
@@ -11507,7 +11280,7 @@ function record(options = {}) {
11507
11280
  else {
11508
11281
  handlers.push(on$1('load', () => {
11509
11282
  wrappedEmit(wrapEvent({
11510
- type: EventType$1.Load,
11283
+ type: EventType.Load,
11511
11284
  data: {},
11512
11285
  }));
11513
11286
  init();
@@ -11526,7 +11299,7 @@ record.addCustomEvent = (tag, payload) => {
11526
11299
  throw new Error('please add custom event after start recording');
11527
11300
  }
11528
11301
  wrappedEmit(wrapEvent({
11529
- type: EventType$1.Custom,
11302
+ type: EventType.Custom,
11530
11303
  data: {
11531
11304
  tag,
11532
11305
  payload,
@@ -11544,503 +11317,6 @@ record.takeFullSnapshot = (isCheckout) => {
11544
11317
  };
11545
11318
  record.mirror = mirror;
11546
11319
 
11547
- /**
11548
- * Create a breadcrumb for a replay.
11549
- */
11550
- function createBreadcrumb(
11551
- breadcrumb,
11552
- ) {
11553
- return {
11554
- timestamp: Date.now() / 1000,
11555
- type: 'default',
11556
- ...breadcrumb,
11557
- };
11558
- }
11559
-
11560
- var NodeType;
11561
- (function (NodeType) {
11562
- NodeType[NodeType["Document"] = 0] = "Document";
11563
- NodeType[NodeType["DocumentType"] = 1] = "DocumentType";
11564
- NodeType[NodeType["Element"] = 2] = "Element";
11565
- NodeType[NodeType["Text"] = 3] = "Text";
11566
- NodeType[NodeType["CDATA"] = 4] = "CDATA";
11567
- NodeType[NodeType["Comment"] = 5] = "Comment";
11568
- })(NodeType || (NodeType = {}));
11569
-
11570
- /* eslint-disable @typescript-eslint/naming-convention */
11571
-
11572
- var EventType; (function (EventType) {
11573
- const DomContentLoaded = 0; EventType[EventType["DomContentLoaded"] = DomContentLoaded] = "DomContentLoaded";
11574
- const Load = 1; EventType[EventType["Load"] = Load] = "Load";
11575
- const FullSnapshot = 2; EventType[EventType["FullSnapshot"] = FullSnapshot] = "FullSnapshot";
11576
- const IncrementalSnapshot = 3; EventType[EventType["IncrementalSnapshot"] = IncrementalSnapshot] = "IncrementalSnapshot";
11577
- const Meta = 4; EventType[EventType["Meta"] = Meta] = "Meta";
11578
- const Custom = 5; EventType[EventType["Custom"] = Custom] = "Custom";
11579
- const Plugin = 6; EventType[EventType["Plugin"] = Plugin] = "Plugin";
11580
- })(EventType || (EventType = {}));
11581
-
11582
- /**
11583
- * This is a partial copy of rrweb's eventWithTime type which only contains the properties
11584
- * we specifcally need in the SDK.
11585
- */
11586
-
11587
- /**
11588
- * Converts a timestamp to ms, if it was in s, or keeps it as ms.
11589
- */
11590
- function timestampToMs(timestamp) {
11591
- const isMs = timestamp > 9999999999;
11592
- return isMs ? timestamp : timestamp * 1000;
11593
- }
11594
-
11595
- /**
11596
- * Add an event to the event buffer.
11597
- * `isCheckout` is true if this is either the very first event, or an event triggered by `checkoutEveryNms`.
11598
- */
11599
- async function addEvent(
11600
- replay,
11601
- event,
11602
- isCheckout,
11603
- ) {
11604
- if (!replay.eventBuffer) {
11605
- // This implies that `_isEnabled` is false
11606
- return null;
11607
- }
11608
-
11609
- if (replay.isPaused()) {
11610
- // Do not add to event buffer when recording is paused
11611
- return null;
11612
- }
11613
-
11614
- const timestampInMs = timestampToMs(event.timestamp);
11615
-
11616
- // Throw out events that happen more than 5 minutes ago. This can happen if
11617
- // page has been left open and idle for a long period of time and user
11618
- // comes back to trigger a new session. The performance entries rely on
11619
- // `performance.timeOrigin`, which is when the page first opened.
11620
- if (timestampInMs + replay.timeouts.sessionIdlePause < Date.now()) {
11621
- return null;
11622
- }
11623
-
11624
- try {
11625
- if (isCheckout) {
11626
- replay.eventBuffer.clear();
11627
- }
11628
-
11629
- const replayOptions = replay.getOptions();
11630
-
11631
- const eventAfterPossibleCallback =
11632
- typeof replayOptions.beforeAddRecordingEvent === 'function' && event.type === EventType.Custom
11633
- ? replayOptions.beforeAddRecordingEvent(event)
11634
- : event;
11635
-
11636
- if (!eventAfterPossibleCallback) {
11637
- return;
11638
- }
11639
-
11640
- return await replay.eventBuffer.addEvent(eventAfterPossibleCallback);
11641
- } catch (error) {
11642
- (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.error(error);
11643
- await replay.stop('addEvent');
11644
-
11645
- const client = getCurrentHub().getClient();
11646
-
11647
- if (client) {
11648
- client.recordDroppedEvent('internal_sdk_error', 'replay');
11649
- }
11650
- }
11651
- }
11652
-
11653
- /**
11654
- * Add a breadcrumb event to replay.
11655
- */
11656
- function addBreadcrumbEvent(replay, breadcrumb) {
11657
- if (breadcrumb.category === 'sentry.transaction') {
11658
- return;
11659
- }
11660
-
11661
- if (['ui.click', 'ui.input'].includes(breadcrumb.category )) {
11662
- replay.triggerUserActivity();
11663
- } else {
11664
- replay.checkAndHandleExpiredSession();
11665
- }
11666
-
11667
- replay.addUpdate(() => {
11668
- void addEvent(replay, {
11669
- type: EventType$1.Custom,
11670
- // TODO: We were converting from ms to seconds for breadcrumbs, spans,
11671
- // but maybe we should just keep them as milliseconds
11672
- timestamp: (breadcrumb.timestamp || 0) * 1000,
11673
- data: {
11674
- tag: 'breadcrumb',
11675
- // normalize to max. 10 depth and 1_000 properties per object
11676
- payload: normalize(breadcrumb, 10, 1000),
11677
- },
11678
- });
11679
-
11680
- // Do not flush after console log messages
11681
- return breadcrumb.category === 'console';
11682
- });
11683
- }
11684
-
11685
- /**
11686
- * Detect a slow click on a button/a tag,
11687
- * and potentially create a corresponding breadcrumb.
11688
- */
11689
- function detectSlowClick(
11690
- replay,
11691
- config,
11692
- clickBreadcrumb,
11693
- node,
11694
- ) {
11695
- if (ignoreElement(node, config)) {
11696
- return;
11697
- }
11698
-
11699
- /*
11700
- We consider a slow click a click on a button/a, which does not trigger one of:
11701
- - DOM mutation
11702
- - Scroll (within 100ms)
11703
- Within the given threshold time.
11704
- After time timeout time, we stop listening and mark it as a slow click anyhow.
11705
- */
11706
-
11707
- let cleanup = () => {
11708
- // replaced further down
11709
- };
11710
-
11711
- // After timeout time, def. consider this a slow click, and stop watching for mutations
11712
- const timeout = setTimeout(() => {
11713
- handleSlowClick(replay, clickBreadcrumb, config.timeout, 'timeout');
11714
- cleanup();
11715
- }, config.timeout);
11716
-
11717
- const mutationHandler = () => {
11718
- maybeHandleSlowClick(replay, clickBreadcrumb, config.threshold, config.timeout, 'mutation');
11719
- cleanup();
11720
- };
11721
-
11722
- const scrollHandler = () => {
11723
- maybeHandleSlowClick(replay, clickBreadcrumb, config.scrollTimeout, config.timeout, 'scroll');
11724
- cleanup();
11725
- };
11726
-
11727
- const obs = new MutationObserver(mutationHandler);
11728
-
11729
- obs.observe(WINDOW.document.documentElement, {
11730
- attributes: true,
11731
- characterData: true,
11732
- childList: true,
11733
- subtree: true,
11734
- });
11735
-
11736
- WINDOW.addEventListener('scroll', scrollHandler);
11737
-
11738
- // Stop listening to scroll timeouts early
11739
- const scrollTimeout = setTimeout(() => {
11740
- WINDOW.removeEventListener('scroll', scrollHandler);
11741
- }, config.scrollTimeout);
11742
-
11743
- cleanup = () => {
11744
- clearTimeout(timeout);
11745
- clearTimeout(scrollTimeout);
11746
- obs.disconnect();
11747
- WINDOW.removeEventListener('scroll', scrollHandler);
11748
- };
11749
- }
11750
-
11751
- function maybeHandleSlowClick(
11752
- replay,
11753
- clickBreadcrumb,
11754
- threshold,
11755
- timeout,
11756
- endReason,
11757
- ) {
11758
- const now = Date.now();
11759
- const timeAfterClickMs = now - clickBreadcrumb.timestamp * 1000;
11760
-
11761
- if (timeAfterClickMs > threshold) {
11762
- handleSlowClick(replay, clickBreadcrumb, Math.min(timeAfterClickMs, timeout), endReason);
11763
- return true;
11764
- }
11765
-
11766
- return false;
11767
- }
11768
-
11769
- function handleSlowClick(
11770
- replay,
11771
- clickBreadcrumb,
11772
- timeAfterClickMs,
11773
- endReason,
11774
- ) {
11775
- const breadcrumb = {
11776
- message: clickBreadcrumb.message,
11777
- timestamp: clickBreadcrumb.timestamp,
11778
- category: 'ui.slowClickDetected',
11779
- data: {
11780
- ...clickBreadcrumb.data,
11781
- url: WINDOW.location.href,
11782
- // TODO FN: add parametrized route, when possible
11783
- timeAfterClickMs,
11784
- endReason,
11785
- },
11786
- };
11787
-
11788
- addBreadcrumbEvent(replay, breadcrumb);
11789
- }
11790
-
11791
- const SLOW_CLICK_IGNORE_TAGS = ['SELECT', 'OPTION'];
11792
-
11793
- function ignoreElement(node, config) {
11794
- // If <input> tag, we only want to consider input[type='submit'] & input[type='button']
11795
- if (node.tagName === 'INPUT' && !['submit', 'button'].includes(node.getAttribute('type') || '')) {
11796
- return true;
11797
- }
11798
-
11799
- if (SLOW_CLICK_IGNORE_TAGS.includes(node.tagName)) {
11800
- return true;
11801
- }
11802
-
11803
- // If <a> tag, detect special variants that may not lead to an action
11804
- // If target !== _self, we may open the link somewhere else, which would lead to no action
11805
- // Also, when downloading a file, we may not leave the page, but still not trigger an action
11806
- if (
11807
- node.tagName === 'A' &&
11808
- (node.hasAttribute('download') || (node.hasAttribute('target') && node.getAttribute('target') !== '_self'))
11809
- ) {
11810
- return true;
11811
- }
11812
-
11813
- if (config.ignoreSelector && node.matches(config.ignoreSelector)) {
11814
- return true;
11815
- }
11816
-
11817
- return false;
11818
- }
11819
-
11820
- // Note that these are the serialized attributes and not attributes directly on
11821
- // the DOM Node. Attributes we are interested in:
11822
- const ATTRIBUTES_TO_RECORD = new Set([
11823
- 'id',
11824
- 'class',
11825
- 'aria-label',
11826
- 'role',
11827
- 'name',
11828
- 'alt',
11829
- 'title',
11830
- 'data-test-id',
11831
- 'data-testid',
11832
- ]);
11833
-
11834
- /**
11835
- * Inclusion list of attributes that we want to record from the DOM element
11836
- */
11837
- function getAttributesToRecord(attributes) {
11838
- const obj = {};
11839
- for (const key in attributes) {
11840
- if (ATTRIBUTES_TO_RECORD.has(key)) {
11841
- let normalizedKey = key;
11842
-
11843
- if (key === 'data-testid' || key === 'data-test-id') {
11844
- normalizedKey = 'testId';
11845
- }
11846
-
11847
- obj[normalizedKey] = attributes[key];
11848
- }
11849
- }
11850
-
11851
- return obj;
11852
- }
11853
-
11854
- const handleDomListener = (
11855
- replay,
11856
- ) => {
11857
- const slowClickExperiment = replay.getOptions()._experiments.slowClicks;
11858
-
11859
- const slowClickConfig = slowClickExperiment
11860
- ? {
11861
- threshold: slowClickExperiment.threshold,
11862
- timeout: slowClickExperiment.timeout,
11863
- scrollTimeout: slowClickExperiment.scrollTimeout,
11864
- ignoreSelector: slowClickExperiment.ignoreSelectors ? slowClickExperiment.ignoreSelectors.join(',') : '',
11865
- }
11866
- : undefined;
11867
-
11868
- return (handlerData) => {
11869
- if (!replay.isEnabled()) {
11870
- return;
11871
- }
11872
-
11873
- const result = handleDom(handlerData);
11874
-
11875
- if (!result) {
11876
- return;
11877
- }
11878
-
11879
- const isClick = handlerData.name === 'click';
11880
- const event = isClick && (handlerData.event );
11881
- // Ignore clicks if ctrl/alt/meta keys are held down as they alter behavior of clicks (e.g. open in new tab)
11882
- if (isClick && slowClickConfig && event && !event.altKey && !event.metaKey && !event.ctrlKey) {
11883
- detectSlowClick(
11884
- replay,
11885
- slowClickConfig,
11886
- result ,
11887
- getClickTargetNode(handlerData.event) ,
11888
- );
11889
- }
11890
-
11891
- addBreadcrumbEvent(replay, result);
11892
- };
11893
- };
11894
-
11895
- /** Get the base DOM breadcrumb. */
11896
- function getBaseDomBreadcrumb(target, message) {
11897
- // `__sn` property is the serialized node created by rrweb
11898
- const serializedNode = target && isRrwebNode(target) && target.__sn.type === NodeType.Element ? target.__sn : null;
11899
-
11900
- return {
11901
- message,
11902
- data: serializedNode
11903
- ? {
11904
- nodeId: serializedNode.id,
11905
- node: {
11906
- id: serializedNode.id,
11907
- tagName: serializedNode.tagName,
11908
- textContent: target
11909
- ? Array.from(target.childNodes)
11910
- .map(
11911
- (node) => '__sn' in node && node.__sn.type === NodeType.Text && node.__sn.textContent,
11912
- )
11913
- .filter(Boolean) // filter out empty values
11914
- .map(text => (text ).trim())
11915
- .join('')
11916
- : '',
11917
- attributes: getAttributesToRecord(serializedNode.attributes),
11918
- },
11919
- }
11920
- : {},
11921
- };
11922
- }
11923
-
11924
- /**
11925
- * An event handler to react to DOM events.
11926
- * Exported for tests.
11927
- */
11928
- function handleDom(handlerData) {
11929
- const { target, message } = getDomTarget(handlerData);
11930
-
11931
- return createBreadcrumb({
11932
- category: `ui.${handlerData.name}`,
11933
- ...getBaseDomBreadcrumb(target, message),
11934
- });
11935
- }
11936
-
11937
- function getDomTarget(handlerData) {
11938
- const isClick = handlerData.name === 'click';
11939
-
11940
- let message;
11941
- let target = null;
11942
-
11943
- // Accessing event.target can throw (see getsentry/raven-js#838, #768)
11944
- try {
11945
- target = isClick ? getClickTargetNode(handlerData.event) : getTargetNode(handlerData.event);
11946
- message = htmlTreeAsString(target, { maxStringLength: 200 }) || '<unknown>';
11947
- } catch (e) {
11948
- message = '<unknown>';
11949
- }
11950
-
11951
- return { target, message };
11952
- }
11953
-
11954
- function isRrwebNode(node) {
11955
- return '__sn' in node;
11956
- }
11957
-
11958
- function getTargetNode(event) {
11959
- if (isEventWithTarget(event)) {
11960
- return event.target ;
11961
- }
11962
-
11963
- return event;
11964
- }
11965
-
11966
- const INTERACTIVE_SELECTOR = 'button,a';
11967
-
11968
- // For clicks, we check if the target is inside of a button or link
11969
- // If so, we use this as the target instead
11970
- // This is useful because if you click on the image in <button><img></button>,
11971
- // The target will be the image, not the button, which we don't want here
11972
- function getClickTargetNode(event) {
11973
- const target = getTargetNode(event);
11974
-
11975
- if (!target || !(target instanceof Element)) {
11976
- return target;
11977
- }
11978
-
11979
- const closestInteractive = target.closest(INTERACTIVE_SELECTOR);
11980
- return closestInteractive || target;
11981
- }
11982
-
11983
- function isEventWithTarget(event) {
11984
- return typeof event === 'object' && !!event && 'target' in event;
11985
- }
11986
-
11987
- /** Handle keyboard events & create breadcrumbs. */
11988
- function handleKeyboardEvent(replay, event) {
11989
- if (!replay.isEnabled()) {
11990
- return;
11991
- }
11992
-
11993
- replay.triggerUserActivity();
11994
-
11995
- const breadcrumb = getKeyboardBreadcrumb(event);
11996
-
11997
- if (!breadcrumb) {
11998
- return;
11999
- }
12000
-
12001
- addBreadcrumbEvent(replay, breadcrumb);
12002
- }
12003
-
12004
- /** exported only for tests */
12005
- function getKeyboardBreadcrumb(event) {
12006
- const { metaKey, shiftKey, ctrlKey, altKey, key, target } = event;
12007
-
12008
- // never capture for input fields
12009
- if (!target || isInputElement(target )) {
12010
- return null;
12011
- }
12012
-
12013
- // Note: We do not consider shift here, as that means "uppercase"
12014
- const hasModifierKey = metaKey || ctrlKey || altKey;
12015
- const isCharacterKey = key.length === 1; // other keys like Escape, Tab, etc have a longer length
12016
-
12017
- // Do not capture breadcrumb if only a word key is pressed
12018
- // This could leak e.g. user input
12019
- if (!hasModifierKey && isCharacterKey) {
12020
- return null;
12021
- }
12022
-
12023
- const message = htmlTreeAsString(target, { maxStringLength: 200 }) || '<unknown>';
12024
- const baseBreadcrumb = getBaseDomBreadcrumb(target , message);
12025
-
12026
- return createBreadcrumb({
12027
- category: 'ui.keyDown',
12028
- message,
12029
- data: {
12030
- ...baseBreadcrumb.data,
12031
- metaKey,
12032
- shiftKey,
12033
- ctrlKey,
12034
- altKey,
12035
- key,
12036
- },
12037
- });
12038
- }
12039
-
12040
- function isInputElement(target) {
12041
- return target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable;
12042
- }
12043
-
12044
11320
  const NAVIGATION_ENTRY_KEYS = [
12045
11321
  'name',
12046
11322
  'type',
@@ -12194,19 +11470,20 @@ class EventBufferArray {
12194
11470
  return this.events.length > 0;
12195
11471
  }
12196
11472
 
12197
- /** @inheritdoc */
12198
- get type() {
12199
- return 'sync';
12200
- }
12201
-
12202
11473
  /** @inheritdoc */
12203
11474
  destroy() {
12204
11475
  this.events = [];
12205
11476
  }
12206
11477
 
12207
11478
  /** @inheritdoc */
12208
- async addEvent(event) {
11479
+ async addEvent(event, isCheckout) {
11480
+ if (isCheckout) {
11481
+ this.events = [event];
11482
+ return;
11483
+ }
11484
+
12209
11485
  this.events.push(event);
11486
+ return;
12210
11487
  }
12211
11488
 
12212
11489
  /** @inheritdoc */
@@ -12220,22 +11497,6 @@ class EventBufferArray {
12220
11497
  resolve(JSON.stringify(eventsRet));
12221
11498
  });
12222
11499
  }
12223
-
12224
- /** @inheritdoc */
12225
- clear() {
12226
- this.events = [];
12227
- }
12228
-
12229
- /** @inheritdoc */
12230
- getEarliestTimestamp() {
12231
- const timestamp = this.events.map(event => event.timestamp).sort()[0];
12232
-
12233
- if (!timestamp) {
12234
- return null;
12235
- }
12236
-
12237
- return timestampToMs(timestamp);
12238
- }
12239
11500
  }
12240
11501
 
12241
11502
  /**
@@ -12343,20 +11604,11 @@ class WorkerHandler {
12343
11604
  * Exported only for testing.
12344
11605
  */
12345
11606
  class EventBufferCompressionWorker {
11607
+ /** @inheritdoc */
12346
11608
 
12347
11609
  constructor(worker) {
12348
11610
  this._worker = new WorkerHandler(worker);
12349
- this._earliestTimestamp = null;
12350
- }
12351
-
12352
- /** @inheritdoc */
12353
- get hasEvents() {
12354
- return !!this._earliestTimestamp;
12355
- }
12356
-
12357
- /** @inheritdoc */
12358
- get type() {
12359
- return 'worker';
11611
+ this.hasEvents = false;
12360
11612
  }
12361
11613
 
12362
11614
  /**
@@ -12379,10 +11631,13 @@ class EventBufferCompressionWorker {
12379
11631
  *
12380
11632
  * Returns true if event was successfuly received and processed by worker.
12381
11633
  */
12382
- addEvent(event) {
12383
- const timestamp = timestampToMs(event.timestamp);
12384
- if (!this._earliestTimestamp || timestamp < this._earliestTimestamp) {
12385
- this._earliestTimestamp = timestamp;
11634
+ async addEvent(event, isCheckout) {
11635
+ this.hasEvents = true;
11636
+
11637
+ if (isCheckout) {
11638
+ // This event is a checkout, make sure worker buffer is cleared before
11639
+ // proceeding.
11640
+ await this._clear();
12386
11641
  }
12387
11642
 
12388
11643
  return this._sendEventToWorker(event);
@@ -12395,18 +11650,6 @@ class EventBufferCompressionWorker {
12395
11650
  return this._finishRequest();
12396
11651
  }
12397
11652
 
12398
- /** @inheritdoc */
12399
- clear() {
12400
- this._earliestTimestamp = null;
12401
- // We do not wait on this, as we assume the order of messages is consistent for the worker
12402
- void this._worker.postMessage('clear');
12403
- }
12404
-
12405
- /** @inheritdoc */
12406
- getEarliestTimestamp() {
12407
- return this._earliestTimestamp;
12408
- }
12409
-
12410
11653
  /**
12411
11654
  * Send the event to the worker.
12412
11655
  */
@@ -12420,10 +11663,15 @@ class EventBufferCompressionWorker {
12420
11663
  async _finishRequest() {
12421
11664
  const response = await this._worker.postMessage('finish');
12422
11665
 
12423
- this._earliestTimestamp = null;
11666
+ this.hasEvents = false;
12424
11667
 
12425
11668
  return response;
12426
11669
  }
11670
+
11671
+ /** Clear any pending events from the worker. */
11672
+ _clear() {
11673
+ return this._worker.postMessage('clear');
11674
+ }
12427
11675
  }
12428
11676
 
12429
11677
  /**
@@ -12441,11 +11689,6 @@ class EventBufferProxy {
12441
11689
  this._ensureWorkerIsLoadedPromise = this._ensureWorkerIsLoaded();
12442
11690
  }
12443
11691
 
12444
- /** @inheritdoc */
12445
- get type() {
12446
- return this._used.type;
12447
- }
12448
-
12449
11692
  /** @inheritDoc */
12450
11693
  get hasEvents() {
12451
11694
  return this._used.hasEvents;
@@ -12457,23 +11700,13 @@ class EventBufferProxy {
12457
11700
  this._compression.destroy();
12458
11701
  }
12459
11702
 
12460
- /** @inheritdoc */
12461
- clear() {
12462
- return this._used.clear();
12463
- }
12464
-
12465
- /** @inheritdoc */
12466
- getEarliestTimestamp() {
12467
- return this._used.getEarliestTimestamp();
12468
- }
12469
-
12470
11703
  /**
12471
11704
  * Add an event to the event buffer.
12472
11705
  *
12473
11706
  * Returns true if event was successfully added.
12474
11707
  */
12475
- addEvent(event) {
12476
- return this._used.addEvent(event);
11708
+ addEvent(event, isCheckout) {
11709
+ return this._used.addEvent(event, isCheckout);
12477
11710
  }
12478
11711
 
12479
11712
  /** @inheritDoc */
@@ -12548,31 +11781,6 @@ function createEventBuffer({ useCompression }) {
12548
11781
  return new EventBufferArray();
12549
11782
  }
12550
11783
 
12551
- /**
12552
- * Removes the session from Session Storage and unsets session in replay instance
12553
- */
12554
- function clearSession(replay) {
12555
- deleteSession();
12556
- replay.session = undefined;
12557
- }
12558
-
12559
- /**
12560
- * Deletes a session from storage
12561
- */
12562
- function deleteSession() {
12563
- const hasSessionStorage = 'sessionStorage' in WINDOW;
12564
-
12565
- if (!hasSessionStorage) {
12566
- return;
12567
- }
12568
-
12569
- try {
12570
- WINDOW.sessionStorage.removeItem(REPLAY_SESSION_KEY);
12571
- } catch (e) {
12572
- // Ignore potential SecurityError exceptions
12573
- }
12574
- }
12575
-
12576
11784
  /**
12577
11785
  * Given an initial timestamp and an expiry duration, checks to see if current
12578
11786
  * time should be considered as expired.
@@ -12603,26 +11811,11 @@ function isSessionExpired(session, timeouts, targetTime = +new Date()) {
12603
11811
  // First, check that maximum session length has not been exceeded
12604
11812
  isExpired(session.started, timeouts.maxSessionLife, targetTime) ||
12605
11813
  // check that the idle timeout has not been exceeded (i.e. user has
12606
- // performed an action within the last `sessionIdleExpire` ms)
12607
- isExpired(session.lastActivity, timeouts.sessionIdleExpire, targetTime)
11814
+ // performed an action within the last `idleTimeout` ms)
11815
+ isExpired(session.lastActivity, timeouts.sessionIdle, targetTime)
12608
11816
  );
12609
11817
  }
12610
11818
 
12611
- /**
12612
- * Given a sample rate, returns true if replay should be sampled.
12613
- *
12614
- * 1.0 = 100% sampling
12615
- * 0.0 = 0% sampling
12616
- */
12617
- function isSampled(sampleRate) {
12618
- if (sampleRate === undefined) {
12619
- return false;
12620
- }
12621
-
12622
- // Math.random() returns a number in range of 0 to 1 (inclusive of 0, but not 1)
12623
- return Math.random() < sampleRate;
12624
- }
12625
-
12626
11819
  /**
12627
11820
  * Save a session to session storage.
12628
11821
  */
@@ -12639,6 +11832,21 @@ function saveSession(session) {
12639
11832
  }
12640
11833
  }
12641
11834
 
11835
+ /**
11836
+ * Given a sample rate, returns true if replay should be sampled.
11837
+ *
11838
+ * 1.0 = 100% sampling
11839
+ * 0.0 = 0% sampling
11840
+ */
11841
+ function isSampled(sampleRate) {
11842
+ if (sampleRate === undefined) {
11843
+ return false;
11844
+ }
11845
+
11846
+ // Math.random() returns a number in range of 0 to 1 (inclusive of 0, but not 1)
11847
+ return Math.random() < sampleRate;
11848
+ }
11849
+
12642
11850
  /**
12643
11851
  * Get a session with defaults & applied sampling.
12644
11852
  */
@@ -12657,15 +11865,14 @@ function makeSession(session) {
12657
11865
  lastActivity,
12658
11866
  segmentId,
12659
11867
  sampled,
12660
- shouldRefresh: true,
12661
11868
  };
12662
11869
  }
12663
11870
 
12664
11871
  /**
12665
11872
  * Get the sampled status for a session based on sample rates & current sampled status.
12666
11873
  */
12667
- function getSessionSampleType(sessionSampleRate, allowBuffering) {
12668
- return isSampled(sessionSampleRate) ? 'session' : allowBuffering ? 'buffer' : false;
11874
+ function getSessionSampleType(sessionSampleRate, errorSampleRate) {
11875
+ return isSampled(sessionSampleRate) ? 'session' : isSampled(errorSampleRate) ? 'error' : false;
12669
11876
  }
12670
11877
 
12671
11878
  /**
@@ -12673,8 +11880,8 @@ function getSessionSampleType(sessionSampleRate, allowBuffering) {
12673
11880
  * that all replays will be saved to as attachments. Currently, we only expect
12674
11881
  * one of these Sentry events per "replay session".
12675
11882
  */
12676
- function createSession({ sessionSampleRate, allowBuffering, stickySession = false }) {
12677
- const sampled = getSessionSampleType(sessionSampleRate, allowBuffering);
11883
+ function createSession({ sessionSampleRate, errorSampleRate, stickySession = false }) {
11884
+ const sampled = getSessionSampleType(sessionSampleRate, errorSampleRate);
12678
11885
  const session = makeSession({
12679
11886
  sampled,
12680
11887
  });
@@ -12722,7 +11929,7 @@ function getSession({
12722
11929
  currentSession,
12723
11930
  stickySession,
12724
11931
  sessionSampleRate,
12725
- allowBuffering,
11932
+ errorSampleRate,
12726
11933
  }) {
12727
11934
  // If session exists and is passed, use it instead of always hitting session storage
12728
11935
  const session = currentSession || (stickySession && fetchSession());
@@ -12735,9 +11942,8 @@ function getSession({
12735
11942
 
12736
11943
  if (!isExpired) {
12737
11944
  return { type: 'saved', session };
12738
- } else if (!session.shouldRefresh) {
12739
- // In this case, stop
12740
- // This is the case if we have an error session that is completed (=triggered an error)
11945
+ } else if (session.sampled === 'error') {
11946
+ // Error samples should not be re-created when expired, but instead we stop when the replay is done
12741
11947
  const discardedSession = makeSession({ sampled: false });
12742
11948
  return { type: 'new', session: discardedSession };
12743
11949
  } else {
@@ -12749,12 +11955,65 @@ function getSession({
12749
11955
  const newSession = createSession({
12750
11956
  stickySession,
12751
11957
  sessionSampleRate,
12752
- allowBuffering,
11958
+ errorSampleRate,
12753
11959
  });
12754
11960
 
12755
11961
  return { type: 'new', session: newSession };
12756
11962
  }
12757
11963
 
11964
+ /**
11965
+ * Add an event to the event buffer.
11966
+ * `isCheckout` is true if this is either the very first event, or an event triggered by `checkoutEveryNms`.
11967
+ */
11968
+ async function addEvent(
11969
+ replay,
11970
+ event,
11971
+ isCheckout,
11972
+ ) {
11973
+ if (!replay.eventBuffer) {
11974
+ // This implies that `_isEnabled` is false
11975
+ return null;
11976
+ }
11977
+
11978
+ if (replay.isPaused()) {
11979
+ // Do not add to event buffer when recording is paused
11980
+ return null;
11981
+ }
11982
+
11983
+ // TODO: sadness -- we will want to normalize timestamps to be in ms -
11984
+ // requires coordination with frontend
11985
+ const isMs = event.timestamp > 9999999999;
11986
+ const timestampInMs = isMs ? event.timestamp : event.timestamp * 1000;
11987
+
11988
+ // Throw out events that happen more than 5 minutes ago. This can happen if
11989
+ // page has been left open and idle for a long period of time and user
11990
+ // comes back to trigger a new session. The performance entries rely on
11991
+ // `performance.timeOrigin`, which is when the page first opened.
11992
+ if (timestampInMs + replay.timeouts.sessionIdle < Date.now()) {
11993
+ return null;
11994
+ }
11995
+
11996
+ // Only record earliest event if a new session was created, otherwise it
11997
+ // shouldn't be relevant
11998
+ const earliestEvent = replay.getContext().earliestEvent;
11999
+ if (replay.session && replay.session.segmentId === 0 && (!earliestEvent || timestampInMs < earliestEvent)) {
12000
+ replay.getContext().earliestEvent = timestampInMs;
12001
+ }
12002
+
12003
+ try {
12004
+ return await replay.eventBuffer.addEvent(event, isCheckout);
12005
+ } catch (error) {
12006
+ (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.error(error);
12007
+ replay.stop('addEvent');
12008
+
12009
+ const client = getCurrentHub().getClient();
12010
+
12011
+ if (client) {
12012
+ client.recordDroppedEvent('internal_sdk_error', 'replay');
12013
+ }
12014
+ }
12015
+ }
12016
+
12758
12017
  /** If the event is an error event */
12759
12018
  function isErrorEvent(event) {
12760
12019
  return !event.type;
@@ -12804,21 +12063,31 @@ function handleAfterSendEvent(replay) {
12804
12063
  return;
12805
12064
  }
12806
12065
 
12807
- // Add error to list of errorIds of replay. This is ok to do even if not
12808
- // sampled because context will get reset at next checkout.
12809
- // XXX: There is also a race condition where it's possible to capture an
12810
- // error to Sentry before Replay SDK has loaded, but response returns after
12811
- // it was loaded, and this gets called.
12066
+ // Add error to list of errorIds of replay
12812
12067
  if (event.event_id) {
12813
12068
  replay.getContext().errorIds.add(event.event_id);
12814
12069
  }
12815
12070
 
12816
- // If error event is tagged with replay id it means it was sampled (when in buffer mode)
12071
+ // Trigger error recording
12817
12072
  // Need to be very careful that this does not cause an infinite loop
12818
- if (replay.recordingMode === 'buffer' && event.tags && event.tags.replayId) {
12819
- setTimeout(() => {
12820
- // Capture current event buffer as new replay
12821
- void replay.sendBufferedReplayOrFlush();
12073
+ if (
12074
+ replay.recordingMode === 'error' &&
12075
+ event.exception &&
12076
+ event.message !== UNABLE_TO_SEND_REPLAY // ignore this error because otherwise we could loop indefinitely with trying to capture replay and failing
12077
+ ) {
12078
+ setTimeout(async () => {
12079
+ // Allow flush to complete before resuming as a session recording, otherwise
12080
+ // the checkout from `startRecording` may be included in the payload.
12081
+ // Prefer to keep the error replay as a separate (and smaller) segment
12082
+ // than the session replay.
12083
+ await replay.flushImmediate();
12084
+
12085
+ if (replay.stopRecording()) {
12086
+ // Reset all "capture on error" configuration before
12087
+ // starting a new recording
12088
+ replay.recordingMode = 'session';
12089
+ replay.startRecording();
12090
+ }
12822
12091
  });
12823
12092
  }
12824
12093
  };
@@ -12840,6 +12109,166 @@ function isBaseTransportSend() {
12840
12109
  );
12841
12110
  }
12842
12111
 
12112
+ var NodeType;
12113
+ (function (NodeType) {
12114
+ NodeType[NodeType["Document"] = 0] = "Document";
12115
+ NodeType[NodeType["DocumentType"] = 1] = "DocumentType";
12116
+ NodeType[NodeType["Element"] = 2] = "Element";
12117
+ NodeType[NodeType["Text"] = 3] = "Text";
12118
+ NodeType[NodeType["CDATA"] = 4] = "CDATA";
12119
+ NodeType[NodeType["Comment"] = 5] = "Comment";
12120
+ })(NodeType || (NodeType = {}));
12121
+
12122
+ /**
12123
+ * Create a breadcrumb for a replay.
12124
+ */
12125
+ function createBreadcrumb(
12126
+ breadcrumb,
12127
+ ) {
12128
+ return {
12129
+ timestamp: Date.now() / 1000,
12130
+ type: 'default',
12131
+ ...breadcrumb,
12132
+ };
12133
+ }
12134
+
12135
+ /**
12136
+ * Add a breadcrumb event to replay.
12137
+ */
12138
+ function addBreadcrumbEvent(replay, breadcrumb) {
12139
+ if (breadcrumb.category === 'sentry.transaction') {
12140
+ return;
12141
+ }
12142
+
12143
+ if (['ui.click', 'ui.input'].includes(breadcrumb.category )) {
12144
+ replay.triggerUserActivity();
12145
+ } else {
12146
+ replay.checkAndHandleExpiredSession();
12147
+ }
12148
+
12149
+ replay.addUpdate(() => {
12150
+ void addEvent(replay, {
12151
+ type: EventType.Custom,
12152
+ // TODO: We were converting from ms to seconds for breadcrumbs, spans,
12153
+ // but maybe we should just keep them as milliseconds
12154
+ timestamp: (breadcrumb.timestamp || 0) * 1000,
12155
+ data: {
12156
+ tag: 'breadcrumb',
12157
+ payload: breadcrumb,
12158
+ },
12159
+ });
12160
+
12161
+ // Do not flush after console log messages
12162
+ return breadcrumb.category === 'console';
12163
+ });
12164
+ }
12165
+
12166
+ // Note that these are the serialized attributes and not attributes directly on
12167
+ // the DOM Node. Attributes we are interested in:
12168
+ const ATTRIBUTES_TO_RECORD = new Set([
12169
+ 'id',
12170
+ 'class',
12171
+ 'aria-label',
12172
+ 'role',
12173
+ 'name',
12174
+ 'alt',
12175
+ 'title',
12176
+ 'data-test-id',
12177
+ 'data-testid',
12178
+ ]);
12179
+
12180
+ /**
12181
+ * Inclusion list of attributes that we want to record from the DOM element
12182
+ */
12183
+ function getAttributesToRecord(attributes) {
12184
+ const obj = {};
12185
+ for (const key in attributes) {
12186
+ if (ATTRIBUTES_TO_RECORD.has(key)) {
12187
+ let normalizedKey = key;
12188
+
12189
+ if (key === 'data-testid' || key === 'data-test-id') {
12190
+ normalizedKey = 'testId';
12191
+ }
12192
+
12193
+ obj[normalizedKey] = attributes[key];
12194
+ }
12195
+ }
12196
+
12197
+ return obj;
12198
+ }
12199
+
12200
+ const handleDomListener =
12201
+ (replay) =>
12202
+ (handlerData) => {
12203
+ if (!replay.isEnabled()) {
12204
+ return;
12205
+ }
12206
+
12207
+ const result = handleDom(handlerData);
12208
+
12209
+ if (!result) {
12210
+ return;
12211
+ }
12212
+
12213
+ addBreadcrumbEvent(replay, result);
12214
+ };
12215
+
12216
+ /**
12217
+ * An event handler to react to DOM events.
12218
+ */
12219
+ function handleDom(handlerData) {
12220
+ let target;
12221
+ let targetNode;
12222
+
12223
+ // Accessing event.target can throw (see getsentry/raven-js#838, #768)
12224
+ try {
12225
+ targetNode = getTargetNode(handlerData);
12226
+ target = htmlTreeAsString(targetNode);
12227
+ } catch (e) {
12228
+ target = '<unknown>';
12229
+ }
12230
+
12231
+ // `__sn` property is the serialized node created by rrweb
12232
+ const serializedNode =
12233
+ targetNode && '__sn' in targetNode && targetNode.__sn.type === NodeType.Element ? targetNode.__sn : null;
12234
+
12235
+ return createBreadcrumb({
12236
+ category: `ui.${handlerData.name}`,
12237
+ message: target,
12238
+ data: serializedNode
12239
+ ? {
12240
+ nodeId: serializedNode.id,
12241
+ node: {
12242
+ id: serializedNode.id,
12243
+ tagName: serializedNode.tagName,
12244
+ textContent: targetNode
12245
+ ? Array.from(targetNode.childNodes)
12246
+ .map(
12247
+ (node) => '__sn' in node && node.__sn.type === NodeType.Text && node.__sn.textContent,
12248
+ )
12249
+ .filter(Boolean) // filter out empty values
12250
+ .map(text => (text ).trim())
12251
+ .join('')
12252
+ : '',
12253
+ attributes: getAttributesToRecord(serializedNode.attributes),
12254
+ },
12255
+ }
12256
+ : {},
12257
+ });
12258
+ }
12259
+
12260
+ function getTargetNode(handlerData) {
12261
+ if (isEventWithTarget(handlerData.event)) {
12262
+ return handlerData.event.target;
12263
+ }
12264
+
12265
+ return handlerData.event;
12266
+ }
12267
+
12268
+ function isEventWithTarget(event) {
12269
+ return !!(event ).target;
12270
+ }
12271
+
12843
12272
  /**
12844
12273
  * Returns true if we think the given event is an error originating inside of rrweb.
12845
12274
  */
@@ -12863,30 +12292,6 @@ function isRrwebError(event, hint) {
12863
12292
  });
12864
12293
  }
12865
12294
 
12866
- /**
12867
- * Determine if event should be sampled (only applies in buffer mode).
12868
- * When an event is captured by `hanldleGlobalEvent`, when in buffer mode
12869
- * we determine if we want to sample the error or not.
12870
- */
12871
- function shouldSampleForBufferEvent(replay, event) {
12872
- if (replay.recordingMode !== 'buffer') {
12873
- return false;
12874
- }
12875
-
12876
- // ignore this error because otherwise we could loop indefinitely with
12877
- // trying to capture replay and failing
12878
- if (event.message === UNABLE_TO_SEND_REPLAY) {
12879
- return false;
12880
- }
12881
-
12882
- // Require the event to be an error event & to have an exception
12883
- if (!event.exception || event.type) {
12884
- return false;
12885
- }
12886
-
12887
- return isSampled(replay.getOptions().errorSampleRate);
12888
- }
12889
-
12890
12295
  /**
12891
12296
  * Returns a listener to be added to `addGlobalEventProcessor(listener)`.
12892
12297
  */
@@ -12916,16 +12321,8 @@ function handleGlobalEventListener(
12916
12321
  return null;
12917
12322
  }
12918
12323
 
12919
- // When in buffer mode, we decide to sample here.
12920
- // Later, in `handleAfterSendEvent`, if the replayId is set, we know that we sampled
12921
- // And convert the buffer session to a full session
12922
- const isErrorEventSampled = shouldSampleForBufferEvent(replay, event);
12923
-
12924
- // Tag errors if it has been sampled in buffer mode, or if it is session mode
12925
- // Only tag transactions if in session mode
12926
- const shouldTagReplayId = isErrorEventSampled || replay.recordingMode === 'session';
12927
-
12928
- if (shouldTagReplayId) {
12324
+ // Only tag transactions with replayId if not waiting for an error
12325
+ if (isErrorEvent(event) || (isTransactionEvent(event) && replay.recordingMode === 'session')) {
12929
12326
  event.tags = { ...event.tags, replayId: replay.getSessionId() };
12930
12327
  }
12931
12328
 
@@ -12975,7 +12372,7 @@ function createPerformanceSpans(
12975
12372
  ) {
12976
12373
  return entries.map(({ type, start, end, name, data }) =>
12977
12374
  addEvent(replay, {
12978
- type: EventType$1.Custom,
12375
+ type: EventType.Custom,
12979
12376
  timestamp: start,
12980
12377
  data: {
12981
12378
  tag: 'performanceSpan',
@@ -13124,14 +12521,12 @@ function handleFetchSpanListener(replay) {
13124
12521
  function handleXhr(handlerData) {
13125
12522
  const { startTimestamp, endTimestamp, xhr } = handlerData;
13126
12523
 
13127
- const sentryXhrData = xhr[SENTRY_XHR_DATA_KEY];
13128
-
13129
- if (!startTimestamp || !endTimestamp || !sentryXhrData) {
12524
+ if (!startTimestamp || !endTimestamp || !xhr.__sentry_xhr__) {
13130
12525
  return null;
13131
12526
  }
13132
12527
 
13133
12528
  // This is only used as a fallback, so we know the body sizes are never set here
13134
- const { method, url, status_code: statusCode } = sentryXhrData;
12529
+ const { method, url, status_code: statusCode } = xhr.__sentry_xhr__;
13135
12530
 
13136
12531
  if (url === undefined) {
13137
12532
  return null;
@@ -13164,393 +12559,6 @@ function handleXhrSpanListener(replay) {
13164
12559
  };
13165
12560
  }
13166
12561
 
13167
- const OBJ = 10;
13168
- const OBJ_KEY = 11;
13169
- const OBJ_KEY_STR = 12;
13170
- const OBJ_VAL = 13;
13171
- const OBJ_VAL_STR = 14;
13172
- const OBJ_VAL_COMPLETED = 15;
13173
-
13174
- const ARR = 20;
13175
- const ARR_VAL = 21;
13176
- const ARR_VAL_STR = 22;
13177
- const ARR_VAL_COMPLETED = 23;
13178
-
13179
- const ALLOWED_PRIMITIVES = ['true', 'false', 'null'];
13180
-
13181
- /**
13182
- * Complete an incomplete JSON string.
13183
- * This will ensure that the last element always has a `"~~"` to indicate it was truncated.
13184
- * For example, `[1,2,` will be completed to `[1,2,"~~"]`
13185
- * and `{"aa":"b` will be completed to `{"aa":"b~~"}`
13186
- */
13187
- function completeJson(incompleteJson, stack) {
13188
- if (!stack.length) {
13189
- return incompleteJson;
13190
- }
13191
-
13192
- let json = incompleteJson;
13193
-
13194
- // Most checks are only needed for the last step in the stack
13195
- const lastPos = stack.length - 1;
13196
- const lastStep = stack[lastPos];
13197
-
13198
- json = _fixLastStep(json, lastStep);
13199
-
13200
- // Complete remaining steps - just add closing brackets
13201
- for (let i = lastPos; i >= 0; i--) {
13202
- const step = stack[i];
13203
-
13204
- switch (step) {
13205
- case OBJ:
13206
- json = `${json}}`;
13207
- break;
13208
- case ARR:
13209
- json = `${json}]`;
13210
- break;
13211
- }
13212
- }
13213
-
13214
- return json;
13215
- }
13216
-
13217
- function _fixLastStep(json, lastStep) {
13218
- switch (lastStep) {
13219
- // Object cases
13220
- case OBJ:
13221
- return `${json}"~~":"~~"`;
13222
- case OBJ_KEY:
13223
- return `${json}:"~~"`;
13224
- case OBJ_KEY_STR:
13225
- return `${json}~~":"~~"`;
13226
- case OBJ_VAL:
13227
- return _maybeFixIncompleteObjValue(json);
13228
- case OBJ_VAL_STR:
13229
- return `${json}~~"`;
13230
- case OBJ_VAL_COMPLETED:
13231
- return `${json},"~~":"~~"`;
13232
-
13233
- // Array cases
13234
- case ARR:
13235
- return `${json}"~~"`;
13236
- case ARR_VAL:
13237
- return _maybeFixIncompleteArrValue(json);
13238
- case ARR_VAL_STR:
13239
- return `${json}~~"`;
13240
- case ARR_VAL_COMPLETED:
13241
- return `${json},"~~"`;
13242
- }
13243
-
13244
- return json;
13245
- }
13246
-
13247
- function _maybeFixIncompleteArrValue(json) {
13248
- const pos = _findLastArrayDelimiter(json);
13249
-
13250
- if (pos > -1) {
13251
- const part = json.slice(pos + 1);
13252
-
13253
- if (ALLOWED_PRIMITIVES.includes(part.trim())) {
13254
- return `${json},"~~"`;
13255
- }
13256
-
13257
- // Everything else is replaced with `"~~"`
13258
- return `${json.slice(0, pos + 1)}"~~"`;
13259
- }
13260
-
13261
- // fallback, this shouldn't happen, to be save
13262
- return json;
13263
- }
13264
-
13265
- function _findLastArrayDelimiter(json) {
13266
- for (let i = json.length - 1; i >= 0; i--) {
13267
- const char = json[i];
13268
-
13269
- if (char === ',' || char === '[') {
13270
- return i;
13271
- }
13272
- }
13273
-
13274
- return -1;
13275
- }
13276
-
13277
- function _maybeFixIncompleteObjValue(json) {
13278
- const startPos = json.lastIndexOf(':');
13279
-
13280
- const part = json.slice(startPos + 1);
13281
-
13282
- if (ALLOWED_PRIMITIVES.includes(part.trim())) {
13283
- return `${json},"~~":"~~"`;
13284
- }
13285
-
13286
- // Everything else is replaced with `"~~"`
13287
- // This also means we do not have incomplete numbers, e.g `[1` is replaced with `["~~"]`
13288
- return `${json.slice(0, startPos + 1)}"~~"`;
13289
- }
13290
-
13291
- /**
13292
- * Evaluate an (incomplete) JSON string.
13293
- */
13294
- function evaluateJson(json) {
13295
- const stack = [];
13296
-
13297
- for (let pos = 0; pos < json.length; pos++) {
13298
- _evaluateJsonPos(stack, json, pos);
13299
- }
13300
-
13301
- return stack;
13302
- }
13303
-
13304
- function _evaluateJsonPos(stack, json, pos) {
13305
- const curStep = stack[stack.length - 1];
13306
-
13307
- const char = json[pos];
13308
-
13309
- const whitespaceRegex = /\s/;
13310
-
13311
- if (whitespaceRegex.test(char)) {
13312
- return;
13313
- }
13314
-
13315
- if (char === '"' && !_isEscaped(json, pos)) {
13316
- _handleQuote(stack, curStep);
13317
- return;
13318
- }
13319
-
13320
- switch (char) {
13321
- case '{':
13322
- _handleObj(stack, curStep);
13323
- break;
13324
- case '[':
13325
- _handleArr(stack, curStep);
13326
- break;
13327
- case ':':
13328
- _handleColon(stack, curStep);
13329
- break;
13330
- case ',':
13331
- _handleComma(stack, curStep);
13332
- break;
13333
- case '}':
13334
- _handleObjClose(stack, curStep);
13335
- break;
13336
- case ']':
13337
- _handleArrClose(stack, curStep);
13338
- break;
13339
- }
13340
- }
13341
-
13342
- function _handleQuote(stack, curStep) {
13343
- // End of obj value
13344
- if (curStep === OBJ_VAL_STR) {
13345
- stack.pop();
13346
- stack.push(OBJ_VAL_COMPLETED);
13347
- return;
13348
- }
13349
-
13350
- // End of arr value
13351
- if (curStep === ARR_VAL_STR) {
13352
- stack.pop();
13353
- stack.push(ARR_VAL_COMPLETED);
13354
- return;
13355
- }
13356
-
13357
- // Start of obj value
13358
- if (curStep === OBJ_VAL) {
13359
- stack.push(OBJ_VAL_STR);
13360
- return;
13361
- }
13362
-
13363
- // Start of arr value
13364
- if (curStep === ARR_VAL) {
13365
- stack.push(ARR_VAL_STR);
13366
- return;
13367
- }
13368
-
13369
- // Start of obj key
13370
- if (curStep === OBJ) {
13371
- stack.push(OBJ_KEY_STR);
13372
- return;
13373
- }
13374
-
13375
- // End of obj key
13376
- if (curStep === OBJ_KEY_STR) {
13377
- stack.pop();
13378
- stack.push(OBJ_KEY);
13379
- return;
13380
- }
13381
- }
13382
-
13383
- function _handleObj(stack, curStep) {
13384
- // Initial object
13385
- if (!curStep) {
13386
- stack.push(OBJ);
13387
- return;
13388
- }
13389
-
13390
- // New object as obj value
13391
- if (curStep === OBJ_VAL) {
13392
- stack.push(OBJ);
13393
- return;
13394
- }
13395
-
13396
- // New object as array element
13397
- if (curStep === ARR_VAL) {
13398
- stack.push(OBJ);
13399
- }
13400
-
13401
- // New object as first array element
13402
- if (curStep === ARR) {
13403
- stack.push(OBJ);
13404
- return;
13405
- }
13406
- }
13407
-
13408
- function _handleArr(stack, curStep) {
13409
- // Initial array
13410
- if (!curStep) {
13411
- stack.push(ARR);
13412
- stack.push(ARR_VAL);
13413
- return;
13414
- }
13415
-
13416
- // New array as obj value
13417
- if (curStep === OBJ_VAL) {
13418
- stack.push(ARR);
13419
- stack.push(ARR_VAL);
13420
- return;
13421
- }
13422
-
13423
- // New array as array element
13424
- if (curStep === ARR_VAL) {
13425
- stack.push(ARR);
13426
- stack.push(ARR_VAL);
13427
- }
13428
-
13429
- // New array as first array element
13430
- if (curStep === ARR) {
13431
- stack.push(ARR);
13432
- stack.push(ARR_VAL);
13433
- return;
13434
- }
13435
- }
13436
-
13437
- function _handleColon(stack, curStep) {
13438
- if (curStep === OBJ_KEY) {
13439
- stack.pop();
13440
- stack.push(OBJ_VAL);
13441
- }
13442
- }
13443
-
13444
- function _handleComma(stack, curStep) {
13445
- // Comma after obj value
13446
- if (curStep === OBJ_VAL) {
13447
- stack.pop();
13448
- return;
13449
- }
13450
- if (curStep === OBJ_VAL_COMPLETED) {
13451
- // Pop OBJ_VAL_COMPLETED & OBJ_VAL
13452
- stack.pop();
13453
- stack.pop();
13454
- return;
13455
- }
13456
-
13457
- // Comma after arr value
13458
- if (curStep === ARR_VAL) {
13459
- // do nothing - basically we'd pop ARR_VAL but add it right back
13460
- return;
13461
- }
13462
-
13463
- if (curStep === ARR_VAL_COMPLETED) {
13464
- // Pop ARR_VAL_COMPLETED
13465
- stack.pop();
13466
-
13467
- // basically we'd pop ARR_VAL but add it right back
13468
- return;
13469
- }
13470
- }
13471
-
13472
- function _handleObjClose(stack, curStep) {
13473
- // Empty object {}
13474
- if (curStep === OBJ) {
13475
- stack.pop();
13476
- }
13477
-
13478
- // Object with element
13479
- if (curStep === OBJ_VAL) {
13480
- // Pop OBJ_VAL, OBJ
13481
- stack.pop();
13482
- stack.pop();
13483
- }
13484
-
13485
- // Obj with element
13486
- if (curStep === OBJ_VAL_COMPLETED) {
13487
- // Pop OBJ_VAL_COMPLETED, OBJ_VAL, OBJ
13488
- stack.pop();
13489
- stack.pop();
13490
- stack.pop();
13491
- }
13492
-
13493
- // if was obj value, complete it
13494
- if (stack[stack.length - 1] === OBJ_VAL) {
13495
- stack.push(OBJ_VAL_COMPLETED);
13496
- }
13497
-
13498
- // if was arr value, complete it
13499
- if (stack[stack.length - 1] === ARR_VAL) {
13500
- stack.push(ARR_VAL_COMPLETED);
13501
- }
13502
- }
13503
-
13504
- function _handleArrClose(stack, curStep) {
13505
- // Empty array []
13506
- if (curStep === ARR) {
13507
- stack.pop();
13508
- }
13509
-
13510
- // Array with element
13511
- if (curStep === ARR_VAL) {
13512
- // Pop ARR_VAL, ARR
13513
- stack.pop();
13514
- stack.pop();
13515
- }
13516
-
13517
- // Array with element
13518
- if (curStep === ARR_VAL_COMPLETED) {
13519
- // Pop ARR_VAL_COMPLETED, ARR_VAL, ARR
13520
- stack.pop();
13521
- stack.pop();
13522
- stack.pop();
13523
- }
13524
-
13525
- // if was obj value, complete it
13526
- if (stack[stack.length - 1] === OBJ_VAL) {
13527
- stack.push(OBJ_VAL_COMPLETED);
13528
- }
13529
-
13530
- // if was arr value, complete it
13531
- if (stack[stack.length - 1] === ARR_VAL) {
13532
- stack.push(ARR_VAL_COMPLETED);
13533
- }
13534
- }
13535
-
13536
- function _isEscaped(str, pos) {
13537
- const previousChar = str[pos - 1];
13538
-
13539
- return previousChar === '\\' && !_isEscaped(str, pos - 1);
13540
- }
13541
-
13542
- /* eslint-disable max-lines */
13543
-
13544
- /**
13545
- * Takes an incomplete JSON string, and returns a hopefully valid JSON string.
13546
- * Note that this _can_ fail, so you should check the return value is valid JSON.
13547
- */
13548
- function fixJson(incompleteJson) {
13549
- const stack = evaluateJson(incompleteJson);
13550
-
13551
- return completeJson(incompleteJson, stack);
13552
- }
13553
-
13554
12562
  /** Get the size of a body. */
13555
12563
  function getBodySize(
13556
12564
  body,
@@ -13644,68 +12652,51 @@ function makeNetworkReplayBreadcrumb(
13644
12652
  return result;
13645
12653
  }
13646
12654
 
13647
- /** Build the request or response part of a replay network breadcrumb that was skipped. */
13648
- function buildSkippedNetworkRequestOrResponse(bodySize) {
13649
- return {
13650
- headers: {},
13651
- size: bodySize,
13652
- _meta: {
13653
- warnings: ['URL_SKIPPED'],
13654
- },
13655
- };
12655
+ /** Get either a JSON network body, or a text representation. */
12656
+ function getNetworkBody(bodyText) {
12657
+ if (!bodyText) {
12658
+ return;
12659
+ }
12660
+
12661
+ try {
12662
+ return JSON.parse(bodyText);
12663
+ } catch (e2) {
12664
+ // return text
12665
+ }
12666
+
12667
+ return bodyText;
13656
12668
  }
13657
12669
 
13658
12670
  /** Build the request or response part of a replay network breadcrumb. */
13659
12671
  function buildNetworkRequestOrResponse(
13660
- headers,
13661
12672
  bodySize,
13662
12673
  body,
13663
12674
  ) {
13664
- if (!bodySize && Object.keys(headers).length === 0) {
13665
- return undefined;
13666
- }
13667
-
13668
12675
  if (!bodySize) {
13669
- return {
13670
- headers,
13671
- };
12676
+ return undefined;
13672
12677
  }
13673
12678
 
13674
12679
  if (!body) {
13675
12680
  return {
13676
- headers,
13677
12681
  size: bodySize,
13678
12682
  };
13679
12683
  }
13680
12684
 
13681
12685
  const info = {
13682
- headers,
13683
12686
  size: bodySize,
13684
12687
  };
13685
12688
 
13686
- const { body: normalizedBody, warnings } = normalizeNetworkBody(body);
13687
- info.body = normalizedBody;
13688
- if (warnings.length > 0) {
12689
+ if (bodySize < NETWORK_BODY_MAX_SIZE) {
12690
+ info.body = body;
12691
+ } else {
13689
12692
  info._meta = {
13690
- warnings,
12693
+ errors: ['MAX_BODY_SIZE_EXCEEDED'],
13691
12694
  };
13692
12695
  }
13693
12696
 
13694
12697
  return info;
13695
12698
  }
13696
12699
 
13697
- /** Filter a set of headers */
13698
- function getAllowedHeaders(headers, allowedHeaders) {
13699
- return Object.keys(headers).reduce((filteredHeaders, key) => {
13700
- const normalizedKey = key.toLowerCase();
13701
- // Avoid putting empty strings into the headers
13702
- if (allowedHeaders.includes(normalizedKey) && headers[key]) {
13703
- filteredHeaders[normalizedKey] = headers[key];
13704
- }
13705
- return filteredHeaders;
13706
- }, {});
13707
- }
13708
-
13709
12700
  function _serializeFormData(formData) {
13710
12701
  // This is a bit simplified, but gives us a decent estimate
13711
12702
  // This converts e.g. { name: 'Anne Smith', age: 13 } to 'name=Anne+Smith&age=13'
@@ -13713,78 +12704,6 @@ function _serializeFormData(formData) {
13713
12704
  return new URLSearchParams(formData).toString();
13714
12705
  }
13715
12706
 
13716
- function normalizeNetworkBody(body)
13717
-
13718
- {
13719
- if (!body || typeof body !== 'string') {
13720
- return {
13721
- body,
13722
- warnings: [],
13723
- };
13724
- }
13725
-
13726
- const exceedsSizeLimit = body.length > NETWORK_BODY_MAX_SIZE;
13727
-
13728
- if (_strIsProbablyJson(body)) {
13729
- try {
13730
- const json = exceedsSizeLimit ? fixJson(body.slice(0, NETWORK_BODY_MAX_SIZE)) : body;
13731
- const normalizedBody = JSON.parse(json);
13732
- return {
13733
- body: normalizedBody,
13734
- warnings: exceedsSizeLimit ? ['JSON_TRUNCATED'] : [],
13735
- };
13736
- } catch (e3) {
13737
- return {
13738
- body: exceedsSizeLimit ? `${body.slice(0, NETWORK_BODY_MAX_SIZE)}…` : body,
13739
- warnings: exceedsSizeLimit ? ['INVALID_JSON', 'TEXT_TRUNCATED'] : ['INVALID_JSON'],
13740
- };
13741
- }
13742
- }
13743
-
13744
- return {
13745
- body: exceedsSizeLimit ? `${body.slice(0, NETWORK_BODY_MAX_SIZE)}…` : body,
13746
- warnings: exceedsSizeLimit ? ['TEXT_TRUNCATED'] : [],
13747
- };
13748
- }
13749
-
13750
- function _strIsProbablyJson(str) {
13751
- const first = str[0];
13752
- const last = str[str.length - 1];
13753
-
13754
- // Simple check: If this does not start & end with {} or [], it's not JSON
13755
- return (first === '[' && last === ']') || (first === '{' && last === '}');
13756
- }
13757
-
13758
- /** Match an URL against a list of strings/Regex. */
13759
- function urlMatches(url, urls) {
13760
- const fullUrl = getFullUrl(url);
13761
-
13762
- return stringMatchesSomePattern(fullUrl, urls);
13763
- }
13764
-
13765
- /** exported for tests */
13766
- function getFullUrl(url, baseURI = WINDOW.document.baseURI) {
13767
- // Short circuit for common cases:
13768
- if (url.startsWith('http://') || url.startsWith('https://') || url.startsWith(WINDOW.location.origin)) {
13769
- return url;
13770
- }
13771
- const fixedUrl = new URL(url, baseURI);
13772
-
13773
- // If these do not match, we are not dealing with a relative URL, so just return it
13774
- if (fixedUrl.origin !== new URL(baseURI).origin) {
13775
- return url;
13776
- }
13777
-
13778
- const fullUrl = fixedUrl.href;
13779
-
13780
- // Remove trailing slashes, if they don't match the original URL
13781
- if (!url.endsWith('/') && fullUrl.endsWith('/')) {
13782
- return fullUrl.slice(0, -1);
13783
- }
13784
-
13785
- return fullUrl;
13786
- }
13787
-
13788
12707
  /**
13789
12708
  * Capture a fetch breadcrumb to a replay.
13790
12709
  * This adds additional data (where approriate).
@@ -13792,9 +12711,7 @@ function getFullUrl(url, baseURI = WINDOW.document.baseURI) {
13792
12711
  async function captureFetchBreadcrumbToReplay(
13793
12712
  breadcrumb,
13794
12713
  hint,
13795
- options
13796
-
13797
- ,
12714
+ options,
13798
12715
  ) {
13799
12716
  try {
13800
12717
  const data = await _prepareFetchData(breadcrumb, hint, options);
@@ -13821,7 +12738,6 @@ function enrichFetchBreadcrumb(
13821
12738
 
13822
12739
  const body = _getFetchRequestArgBody(input);
13823
12740
  const reqSize = getBodySize(body, options.textEncoder);
13824
-
13825
12741
  const resSize = response ? parseContentLengthHeader(response.headers.get('content-length')) : undefined;
13826
12742
 
13827
12743
  if (reqSize !== undefined) {
@@ -13835,109 +12751,97 @@ function enrichFetchBreadcrumb(
13835
12751
  async function _prepareFetchData(
13836
12752
  breadcrumb,
13837
12753
  hint,
13838
- options
13839
-
13840
- ,
12754
+ options,
13841
12755
  ) {
13842
12756
  const { startTimestamp, endTimestamp } = hint;
13843
12757
 
13844
12758
  const {
13845
12759
  url,
13846
12760
  method,
13847
- status_code: statusCode = 0,
12761
+ status_code: statusCode,
13848
12762
  request_body_size: requestBodySize,
13849
12763
  response_body_size: responseBodySize,
13850
12764
  } = breadcrumb.data;
13851
12765
 
13852
- const captureDetails = urlMatches(url, options.networkDetailAllowUrls);
13853
-
13854
- const request = captureDetails
13855
- ? _getRequestInfo(options, hint.input, requestBodySize)
13856
- : buildSkippedNetworkRequestOrResponse(requestBodySize);
13857
- const response = await _getResponseInfo(captureDetails, options, hint.response, responseBodySize);
12766
+ const request = _getRequestInfo(options, hint.input, requestBodySize);
12767
+ const response = await _getResponseInfo(options, hint.response, responseBodySize);
13858
12768
 
13859
12769
  return {
13860
12770
  startTimestamp,
13861
12771
  endTimestamp,
13862
12772
  url,
13863
12773
  method,
13864
- statusCode,
12774
+ statusCode: statusCode || 0,
13865
12775
  request,
13866
12776
  response,
13867
12777
  };
13868
12778
  }
13869
12779
 
13870
12780
  function _getRequestInfo(
13871
- { networkCaptureBodies, networkRequestHeaders },
12781
+ { captureBodies },
13872
12782
  input,
13873
12783
  requestBodySize,
13874
12784
  ) {
13875
- const headers = getRequestHeaders(input, networkRequestHeaders);
13876
-
13877
- if (!networkCaptureBodies) {
13878
- return buildNetworkRequestOrResponse(headers, requestBodySize, undefined);
12785
+ if (!captureBodies) {
12786
+ return buildNetworkRequestOrResponse(requestBodySize, undefined);
13879
12787
  }
13880
12788
 
13881
12789
  // We only want to transmit string or string-like bodies
13882
12790
  const requestBody = _getFetchRequestArgBody(input);
13883
- const bodyStr = getBodyString(requestBody);
13884
- return buildNetworkRequestOrResponse(headers, requestBodySize, bodyStr);
12791
+ const body = getNetworkBody(getBodyString(requestBody));
12792
+ return buildNetworkRequestOrResponse(requestBodySize, body);
13885
12793
  }
13886
12794
 
13887
12795
  async function _getResponseInfo(
13888
- captureDetails,
13889
- {
13890
- networkCaptureBodies,
13891
- textEncoder,
13892
- networkResponseHeaders,
13893
- }
13894
-
13895
- ,
12796
+ { captureBodies, textEncoder },
13896
12797
  response,
13897
12798
  responseBodySize,
13898
12799
  ) {
13899
- if (!captureDetails && responseBodySize !== undefined) {
13900
- return buildSkippedNetworkRequestOrResponse(responseBodySize);
13901
- }
13902
-
13903
- const headers = getAllHeaders(response.headers, networkResponseHeaders);
13904
-
13905
- if (!networkCaptureBodies && responseBodySize !== undefined) {
13906
- return buildNetworkRequestOrResponse(headers, responseBodySize, undefined);
12800
+ if (!captureBodies && responseBodySize !== undefined) {
12801
+ return buildNetworkRequestOrResponse(responseBodySize, undefined);
13907
12802
  }
13908
12803
 
13909
12804
  // Only clone the response if we need to
13910
12805
  try {
13911
12806
  // We have to clone this, as the body can only be read once
13912
12807
  const res = response.clone();
13913
- const bodyText = await _parseFetchBody(res);
12808
+ const { body, bodyText } = await _parseFetchBody(res);
13914
12809
 
13915
12810
  const size =
13916
12811
  bodyText && bodyText.length && responseBodySize === undefined
13917
12812
  ? getBodySize(bodyText, textEncoder)
13918
12813
  : responseBodySize;
13919
12814
 
13920
- if (!captureDetails) {
13921
- return buildSkippedNetworkRequestOrResponse(size);
13922
- }
13923
-
13924
- if (networkCaptureBodies) {
13925
- return buildNetworkRequestOrResponse(headers, size, bodyText);
12815
+ if (captureBodies) {
12816
+ return buildNetworkRequestOrResponse(size, body);
13926
12817
  }
13927
12818
 
13928
- return buildNetworkRequestOrResponse(headers, size, undefined);
12819
+ return buildNetworkRequestOrResponse(size, undefined);
13929
12820
  } catch (e) {
13930
12821
  // fallback
13931
- return buildNetworkRequestOrResponse(headers, responseBodySize, undefined);
12822
+ return buildNetworkRequestOrResponse(responseBodySize, undefined);
13932
12823
  }
13933
12824
  }
13934
12825
 
13935
- async function _parseFetchBody(response) {
12826
+ async function _parseFetchBody(
12827
+ response,
12828
+ ) {
12829
+ let bodyText;
12830
+
13936
12831
  try {
13937
- return await response.text();
12832
+ bodyText = await response.text();
13938
12833
  } catch (e2) {
13939
- return undefined;
12834
+ return {};
13940
12835
  }
12836
+
12837
+ try {
12838
+ const body = JSON.parse(bodyText);
12839
+ return { body, bodyText };
12840
+ } catch (e3) {
12841
+ // just send bodyText
12842
+ }
12843
+
12844
+ return { bodyText, body: bodyText };
13941
12845
  }
13942
12846
 
13943
12847
  function _getFetchRequestArgBody(fetchArgs = []) {
@@ -13949,56 +12853,6 @@ function _getFetchRequestArgBody(fetchArgs = []) {
13949
12853
  return (fetchArgs[1] ).body;
13950
12854
  }
13951
12855
 
13952
- function getAllHeaders(headers, allowedHeaders) {
13953
- const allHeaders = {};
13954
-
13955
- allowedHeaders.forEach(header => {
13956
- if (headers.get(header)) {
13957
- allHeaders[header] = headers.get(header) ;
13958
- }
13959
- });
13960
-
13961
- return allHeaders;
13962
- }
13963
-
13964
- function getRequestHeaders(fetchArgs, allowedHeaders) {
13965
- if (fetchArgs.length === 1 && typeof fetchArgs[0] !== 'string') {
13966
- return getHeadersFromOptions(fetchArgs[0] , allowedHeaders);
13967
- }
13968
-
13969
- if (fetchArgs.length === 2) {
13970
- return getHeadersFromOptions(fetchArgs[1] , allowedHeaders);
13971
- }
13972
-
13973
- return {};
13974
- }
13975
-
13976
- function getHeadersFromOptions(
13977
- input,
13978
- allowedHeaders,
13979
- ) {
13980
- if (!input) {
13981
- return {};
13982
- }
13983
-
13984
- const headers = input.headers;
13985
-
13986
- if (!headers) {
13987
- return {};
13988
- }
13989
-
13990
- if (headers instanceof Headers) {
13991
- return getAllHeaders(headers, allowedHeaders);
13992
- }
13993
-
13994
- // We do not support this, as it is not really documented (anymore?)
13995
- if (Array.isArray(headers)) {
13996
- return {};
13997
- }
13998
-
13999
- return getAllowedHeaders(headers, allowedHeaders);
14000
- }
14001
-
14002
12856
  /**
14003
12857
  * Capture an XHR breadcrumb to a replay.
14004
12858
  * This adds additional data (where approriate).
@@ -14049,12 +12903,12 @@ function _prepareXhrData(
14049
12903
  hint,
14050
12904
  options,
14051
12905
  ) {
14052
- const { startTimestamp, endTimestamp, input, xhr } = hint;
12906
+ const { startTimestamp, endTimestamp, input } = hint;
14053
12907
 
14054
12908
  const {
14055
12909
  url,
14056
12910
  method,
14057
- status_code: statusCode = 0,
12911
+ status_code: statusCode,
14058
12912
  request_body_size: requestBodySize,
14059
12913
  response_body_size: responseBodySize,
14060
12914
  } = breadcrumb.data;
@@ -14063,35 +12917,13 @@ function _prepareXhrData(
14063
12917
  return null;
14064
12918
  }
14065
12919
 
14066
- if (!urlMatches(url, options.networkDetailAllowUrls)) {
14067
- const request = buildSkippedNetworkRequestOrResponse(requestBodySize);
14068
- const response = buildSkippedNetworkRequestOrResponse(responseBodySize);
14069
- return {
14070
- startTimestamp,
14071
- endTimestamp,
14072
- url,
14073
- method,
14074
- statusCode,
14075
- request,
14076
- response,
14077
- };
14078
- }
14079
-
14080
- const xhrInfo = xhr[SENTRY_XHR_DATA_KEY];
14081
- const networkRequestHeaders = xhrInfo
14082
- ? getAllowedHeaders(xhrInfo.request_headers, options.networkRequestHeaders)
14083
- : {};
14084
- const networkResponseHeaders = getAllowedHeaders(getResponseHeaders(xhr), options.networkResponseHeaders);
14085
-
14086
12920
  const request = buildNetworkRequestOrResponse(
14087
- networkRequestHeaders,
14088
12921
  requestBodySize,
14089
- options.networkCaptureBodies ? getBodyString(input) : undefined,
12922
+ options.captureBodies ? getNetworkBody(getBodyString(input)) : undefined,
14090
12923
  );
14091
12924
  const response = buildNetworkRequestOrResponse(
14092
- networkResponseHeaders,
14093
12925
  responseBodySize,
14094
- options.networkCaptureBodies ? hint.xhr.responseText : undefined,
12926
+ options.captureBodies ? getNetworkBody(hint.xhr.responseText) : undefined,
14095
12927
  );
14096
12928
 
14097
12929
  return {
@@ -14099,26 +12931,12 @@ function _prepareXhrData(
14099
12931
  endTimestamp,
14100
12932
  url,
14101
12933
  method,
14102
- statusCode,
12934
+ statusCode: statusCode || 0,
14103
12935
  request,
14104
12936
  response,
14105
12937
  };
14106
12938
  }
14107
12939
 
14108
- function getResponseHeaders(xhr) {
14109
- const headers = xhr.getAllResponseHeaders();
14110
-
14111
- if (!headers) {
14112
- return {};
14113
- }
14114
-
14115
- return headers.split('\r\n').reduce((acc, line) => {
14116
- const [key, value] = line.split(': ');
14117
- acc[key.toLowerCase()] = value;
14118
- return acc;
14119
- }, {});
14120
- }
14121
-
14122
12940
  /**
14123
12941
  * This method does two things:
14124
12942
  * - It enriches the regular XHR/fetch breadcrumbs with request/response size data
@@ -14131,16 +12949,10 @@ function handleNetworkBreadcrumbs(replay) {
14131
12949
  try {
14132
12950
  const textEncoder = new TextEncoder();
14133
12951
 
14134
- const { networkDetailAllowUrls, networkCaptureBodies, networkRequestHeaders, networkResponseHeaders } =
14135
- replay.getOptions();
14136
-
14137
12952
  const options = {
14138
12953
  replay,
14139
12954
  textEncoder,
14140
- networkDetailAllowUrls,
14141
- networkCaptureBodies,
14142
- networkRequestHeaders,
14143
- networkResponseHeaders,
12955
+ captureBodies: replay.getOptions()._experiments.captureNetworkBodies || false,
14144
12956
  };
14145
12957
 
14146
12958
  if (client && client.on) {
@@ -14248,66 +13060,9 @@ function handleScope(scope) {
14248
13060
  return null;
14249
13061
  }
14250
13062
 
14251
- if (newBreadcrumb.category === 'console') {
14252
- return normalizeConsoleBreadcrumb(newBreadcrumb);
14253
- }
14254
-
14255
13063
  return createBreadcrumb(newBreadcrumb);
14256
13064
  }
14257
13065
 
14258
- /** exported for tests only */
14259
- function normalizeConsoleBreadcrumb(breadcrumb) {
14260
- const args = breadcrumb.data && breadcrumb.data.arguments;
14261
-
14262
- if (!Array.isArray(args) || args.length === 0) {
14263
- return createBreadcrumb(breadcrumb);
14264
- }
14265
-
14266
- let isTruncated = false;
14267
-
14268
- // Avoid giant args captures
14269
- const normalizedArgs = args.map(arg => {
14270
- if (!arg) {
14271
- return arg;
14272
- }
14273
- if (typeof arg === 'string') {
14274
- if (arg.length > CONSOLE_ARG_MAX_SIZE) {
14275
- isTruncated = true;
14276
- return `${arg.slice(0, CONSOLE_ARG_MAX_SIZE)}…`;
14277
- }
14278
-
14279
- return arg;
14280
- }
14281
- if (typeof arg === 'object') {
14282
- try {
14283
- const normalizedArg = normalize(arg, 7);
14284
- const stringified = JSON.stringify(normalizedArg);
14285
- if (stringified.length > CONSOLE_ARG_MAX_SIZE) {
14286
- const fixedJson = fixJson(stringified.slice(0, CONSOLE_ARG_MAX_SIZE));
14287
- const json = JSON.parse(fixedJson);
14288
- // We only set this after JSON.parse() was successfull, so we know we didn't run into `catch`
14289
- isTruncated = true;
14290
- return json;
14291
- }
14292
- return normalizedArg;
14293
- } catch (e) {
14294
- // fall back to default
14295
- }
14296
- }
14297
-
14298
- return arg;
14299
- });
14300
-
14301
- return createBreadcrumb({
14302
- ...breadcrumb,
14303
- data: {
14304
- ...breadcrumb.data,
14305
- arguments: normalizedArgs,
14306
- ...(isTruncated ? { _meta: { warnings: ['CONSOLE_ARG_TRUNCATED'] } } : {}),
14307
- },
14308
- });
14309
- }
14310
-
14311
13066
  /**
14312
13067
  * Add global listeners that cannot be removed.
14313
13068
  */
@@ -14332,8 +13087,7 @@ function addGlobalListeners(replay) {
14332
13087
  client.on('afterSendEvent', handleAfterSendEvent(replay));
14333
13088
  client.on('createDsc', (dsc) => {
14334
13089
  const replayId = replay.getSessionId();
14335
- // We do not want to set the DSC when in buffer mode, as that means the replay has not been sent (yet)
14336
- if (replayId && replay.isEnabled() && replay.recordingMode === 'session') {
13090
+ if (replayId) {
14337
13091
  dsc.replay_id = replayId;
14338
13092
  }
14339
13093
  });
@@ -14641,7 +13395,7 @@ function getHandleRecordingEmit(replay) {
14641
13395
  // when an error occurs. Clear any state that happens before this current
14642
13396
  // checkout. This needs to happen before `addEvent()` which updates state
14643
13397
  // dependent on this reset.
14644
- if (replay.recordingMode === 'buffer' && isCheckout) {
13398
+ if (replay.recordingMode === 'error' && isCheckout) {
14645
13399
  replay.setInitialState();
14646
13400
  }
14647
13401
 
@@ -14655,14 +13409,6 @@ function getHandleRecordingEmit(replay) {
14655
13409
  return false;
14656
13410
  }
14657
13411
 
14658
- // Additionally, create a meta event that will capture certain SDK settings.
14659
- // In order to handle buffer mode, this needs to either be done when we
14660
- // receive checkout events or at flush time.
14661
- //
14662
- // `isCheckout` is always true, but want to be explicit that it should
14663
- // only be added for checkouts
14664
- void addSettingsEvent(replay, isCheckout);
14665
-
14666
13412
  // If there is a previousSessionId after a full snapshot occurs, then
14667
13413
  // the replay session was started due to session expiration. The new session
14668
13414
  // is started before triggering a new checkout and contains the id
@@ -14673,10 +13419,10 @@ function getHandleRecordingEmit(replay) {
14673
13419
  return true;
14674
13420
  }
14675
13421
 
14676
- // When in buffer mode, make sure we adjust the session started date to the current earliest event of the buffer
14677
- // this should usually be the timestamp of the checkout event, but to be safe...
14678
- if (replay.recordingMode === 'buffer' && replay.session && replay.eventBuffer) {
14679
- const earliestEvent = replay.eventBuffer.getEarliestTimestamp();
13422
+ // See note above re: session start needs to reflect the most recent
13423
+ // checkout.
13424
+ if (replay.recordingMode === 'error' && replay.session) {
13425
+ const { earliestEvent } = replay.getContext();
14680
13426
  if (earliestEvent) {
14681
13427
  replay.session.started = earliestEvent;
14682
13428
 
@@ -14686,30 +13432,6 @@ function getHandleRecordingEmit(replay) {
14686
13432
  }
14687
13433
  }
14688
13434
 
14689
- const options = replay.getOptions();
14690
-
14691
- // TODO: We want this as an experiment so that we can test
14692
- // internally and create metrics before making this the default
14693
- if (options._experiments.delayFlushOnCheckout) {
14694
- // If the full snapshot is due to an initial load, we will not have
14695
- // a previous session ID. In this case, we want to buffer events
14696
- // for a set amount of time before flushing. This can help avoid
14697
- // capturing replays of users that immediately close the window.
14698
- setTimeout(() => replay.conditionalFlush(), options._experiments.delayFlushOnCheckout);
14699
-
14700
- // Cancel any previously debounced flushes to ensure there are no [near]
14701
- // simultaneous flushes happening. The latter request should be
14702
- // insignificant in this case, so wait for additional user interaction to
14703
- // trigger a new flush.
14704
- //
14705
- // This can happen because there's no guarantee that a recording event
14706
- // happens first. e.g. a mouse click can happen and trigger a debounced
14707
- // flush before the checkout.
14708
- replay.cancelFlush();
14709
-
14710
- return true;
14711
- }
14712
-
14713
13435
  // Flush immediately so that we do not miss the first segment, otherwise
14714
13436
  // it can prevent loading on the UI. This will cause an increase in short
14715
13437
  // replays (e.g. opening and closing a tab quickly), but these can be
@@ -14724,46 +13446,6 @@ function getHandleRecordingEmit(replay) {
14724
13446
  };
14725
13447
  }
14726
13448
 
14727
- /**
14728
- * Exported for tests
14729
- */
14730
- function createOptionsEvent(replay) {
14731
- const options = replay.getOptions();
14732
- return {
14733
- type: EventType.Custom,
14734
- timestamp: Date.now(),
14735
- data: {
14736
- tag: 'options',
14737
- payload: {
14738
- sessionSampleRate: options.sessionSampleRate,
14739
- errorSampleRate: options.errorSampleRate,
14740
- useCompressionOption: options.useCompression,
14741
- blockAllMedia: options.blockAllMedia,
14742
- maskAllText: options.maskAllText,
14743
- maskAllInputs: options.maskAllInputs,
14744
- useCompression: replay.eventBuffer ? replay.eventBuffer.type === 'worker' : false,
14745
- networkDetailHasUrls: options.networkDetailAllowUrls.length > 0,
14746
- networkCaptureBodies: options.networkCaptureBodies,
14747
- networkRequestHasHeaders: options.networkRequestHeaders.length > 0,
14748
- networkResponseHasHeaders: options.networkResponseHeaders.length > 0,
14749
- },
14750
- },
14751
- };
14752
- }
14753
-
14754
- /**
14755
- * Add a "meta" event that contains a simplified view on current configuration
14756
- * options. This should only be included on the first segment of a recording.
14757
- */
14758
- function addSettingsEvent(replay, isCheckout) {
14759
- // Only need to add this event when sending the first segment
14760
- if (!isCheckout || !replay.session || replay.session.segmentId !== 0) {
14761
- return Promise.resolve(null);
14762
- }
14763
-
14764
- return addEvent(replay, createOptionsEvent(replay), false);
14765
- }
14766
-
14767
13449
  /**
14768
13450
  * Create a replay envelope ready to be sent.
14769
13451
  * This includes both the replay event, as well as the recording data.
@@ -14875,9 +13557,11 @@ async function sendReplayRequest({
14875
13557
  recordingData,
14876
13558
  replayId,
14877
13559
  segmentId: segment_id,
13560
+ includeReplayStartTimestamp,
14878
13561
  eventContext,
14879
13562
  timestamp,
14880
13563
  session,
13564
+ options,
14881
13565
  }) {
14882
13566
  const preparedRecordingData = prepareRecordingData({
14883
13567
  recordingData,
@@ -14899,8 +13583,9 @@ async function sendReplayRequest({
14899
13583
  }
14900
13584
 
14901
13585
  const baseEvent = {
13586
+ // @ts-ignore private api
14902
13587
  type: REPLAY_EVENT_NAME,
14903
- replay_start_timestamp: initialTimestamp / 1000,
13588
+ ...(includeReplayStartTimestamp ? { replay_start_timestamp: initialTimestamp / 1000 } : {}),
14904
13589
  timestamp: timestamp / 1000,
14905
13590
  error_ids: errorIds,
14906
13591
  trace_ids: traceIds,
@@ -14919,6 +13604,15 @@ async function sendReplayRequest({
14919
13604
  return;
14920
13605
  }
14921
13606
 
13607
+ replayEvent.contexts = {
13608
+ ...replayEvent.contexts,
13609
+ replay: {
13610
+ ...(replayEvent.contexts && replayEvent.contexts.replay),
13611
+ session_sample_rate: options.sessionSampleRate,
13612
+ error_sample_rate: options.errorSampleRate,
13613
+ },
13614
+ };
13615
+
14922
13616
  /*
14923
13617
  For reference, the fully built event looks something like this:
14924
13618
  {
@@ -14949,6 +13643,10 @@ async function sendReplayRequest({
14949
13643
  },
14950
13644
  "sdkProcessingMetadata": {},
14951
13645
  "contexts": {
13646
+ "replay": {
13647
+ "session_sample_rate": 1,
13648
+ "error_sample_rate": 0,
13649
+ },
14952
13650
  },
14953
13651
  }
14954
13652
  */
@@ -15074,11 +13772,9 @@ class ReplayContainer {
15074
13772
  __init2() {this.performanceEvents = [];}
15075
13773
 
15076
13774
  /**
15077
- * Recording can happen in one of three modes:
15078
- * - session: Record the whole session, sending it continuously
15079
- * - buffer: Always keep the last 60s of recording, requires:
15080
- * - having replaysOnErrorSampleRate > 0 to capture replay when an error occurs
15081
- * - or calling `flush()` to send the replay
13775
+ * Recording can happen in one of two modes:
13776
+ * * session: Record the whole session, sending it continuously
13777
+ * * error: Always keep the last 60s of recording, and when an error occurs, send it immediately
15082
13778
  */
15083
13779
  __init3() {this.recordingMode = 'session';}
15084
13780
 
@@ -15087,8 +13783,7 @@ class ReplayContainer {
15087
13783
  * @hidden
15088
13784
  */
15089
13785
  __init4() {this.timeouts = {
15090
- sessionIdlePause: SESSION_IDLE_PAUSE_DURATION,
15091
- sessionIdleExpire: SESSION_IDLE_EXPIRE_DURATION,
13786
+ sessionIdle: SESSION_IDLE_DURATION,
15092
13787
  maxSessionLife: MAX_SESSION_LIFE,
15093
13788
  }; }
15094
13789
 
@@ -15132,6 +13827,7 @@ class ReplayContainer {
15132
13827
  errorIds: new Set(),
15133
13828
  traceIds: new Set(),
15134
13829
  urls: [],
13830
+ earliestEvent: null,
15135
13831
  initialTimestamp: Date.now(),
15136
13832
  initialUrl: '',
15137
13833
  };}
@@ -15141,7 +13837,7 @@ class ReplayContainer {
15141
13837
  recordingOptions,
15142
13838
  }
15143
13839
 
15144
- ) {ReplayContainer.prototype.__init.call(this);ReplayContainer.prototype.__init2.call(this);ReplayContainer.prototype.__init3.call(this);ReplayContainer.prototype.__init4.call(this);ReplayContainer.prototype.__init5.call(this);ReplayContainer.prototype.__init6.call(this);ReplayContainer.prototype.__init7.call(this);ReplayContainer.prototype.__init8.call(this);ReplayContainer.prototype.__init9.call(this);ReplayContainer.prototype.__init10.call(this);ReplayContainer.prototype.__init11.call(this);ReplayContainer.prototype.__init12.call(this);ReplayContainer.prototype.__init13.call(this);ReplayContainer.prototype.__init14.call(this);ReplayContainer.prototype.__init15.call(this);ReplayContainer.prototype.__init16.call(this);ReplayContainer.prototype.__init17.call(this);ReplayContainer.prototype.__init18.call(this);
13840
+ ) {ReplayContainer.prototype.__init.call(this);ReplayContainer.prototype.__init2.call(this);ReplayContainer.prototype.__init3.call(this);ReplayContainer.prototype.__init4.call(this);ReplayContainer.prototype.__init5.call(this);ReplayContainer.prototype.__init6.call(this);ReplayContainer.prototype.__init7.call(this);ReplayContainer.prototype.__init8.call(this);ReplayContainer.prototype.__init9.call(this);ReplayContainer.prototype.__init10.call(this);ReplayContainer.prototype.__init11.call(this);ReplayContainer.prototype.__init12.call(this);ReplayContainer.prototype.__init13.call(this);ReplayContainer.prototype.__init14.call(this);ReplayContainer.prototype.__init15.call(this);ReplayContainer.prototype.__init16.call(this);ReplayContainer.prototype.__init17.call(this);
15145
13841
  this._recordingOptions = recordingOptions;
15146
13842
  this._options = options;
15147
13843
 
@@ -15171,102 +13867,49 @@ class ReplayContainer {
15171
13867
  }
15172
13868
 
15173
13869
  /**
15174
- * Initializes the plugin based on sampling configuration. Should not be
15175
- * called outside of constructor.
13870
+ * Initializes the plugin.
13871
+ *
13872
+ * Creates or loads a session, attaches listeners to varying events (DOM,
13873
+ * _performanceObserver, Recording, Sentry SDK, etc)
15176
13874
  */
15177
- initializeSampling() {
15178
- const { errorSampleRate, sessionSampleRate } = this._options;
15179
-
15180
- // If neither sample rate is > 0, then do nothing - user will need to call one of
15181
- // `start()` or `startBuffering` themselves.
15182
- if (errorSampleRate <= 0 && sessionSampleRate <= 0) {
15183
- return;
15184
- }
15185
-
15186
- // Otherwise if there is _any_ sample rate set, try to load an existing
15187
- // session, or create a new one.
15188
- const isSessionSampled = this._loadAndCheckSession();
13875
+ start() {
13876
+ this.setInitialState();
15189
13877
 
15190
- if (!isSessionSampled) {
15191
- // This should only occur if `errorSampleRate` is 0 and was unsampled for
15192
- // session-based replay. In this case there is nothing to do.
13878
+ if (!this._loadAndCheckSession()) {
15193
13879
  return;
15194
13880
  }
15195
13881
 
13882
+ // If there is no session, then something bad has happened - can't continue
15196
13883
  if (!this.session) {
15197
- // This should not happen, something wrong has occurred
15198
- this._handleException(new Error('Unable to initialize and create session'));
13884
+ this._handleException(new Error('No session found'));
15199
13885
  return;
15200
13886
  }
15201
13887
 
15202
- if (this.session.sampled && this.session.sampled !== 'session') {
15203
- // If not sampled as session-based, then recording mode will be `buffer`
15204
- // Note that we don't explicitly check if `sampled === 'buffer'` because we
15205
- // could have sessions from Session storage that are still `error` from
15206
- // prior SDK version.
15207
- this.recordingMode = 'buffer';
15208
- }
15209
-
15210
- this._initializeRecording();
15211
- }
15212
-
15213
- /**
15214
- * Start a replay regardless of sampling rate. Calling this will always
15215
- * create a new session. Will throw an error if replay is already in progress.
15216
- *
15217
- * Creates or loads a session, attaches listeners to varying events (DOM,
15218
- * _performanceObserver, Recording, Sentry SDK, etc)
15219
- */
15220
- start() {
15221
- if (this._isEnabled && this.recordingMode === 'session') {
15222
- throw new Error('Replay recording is already in progress');
13888
+ if (!this.session.sampled) {
13889
+ // If session was not sampled, then we do not initialize the integration at all.
13890
+ return;
15223
13891
  }
15224
13892
 
15225
- if (this._isEnabled && this.recordingMode === 'buffer') {
15226
- throw new Error('Replay buffering is in progress, call `flush()` to save the replay');
13893
+ // If session is sampled for errors, then we need to set the recordingMode
13894
+ // to 'error', which will configure recording with different options.
13895
+ if (this.session.sampled === 'error') {
13896
+ this.recordingMode = 'error';
15227
13897
  }
15228
13898
 
15229
- const previousSessionId = this.session && this.session.id;
13899
+ // setup() is generally called on page load or manually - in both cases we
13900
+ // should treat it as an activity
13901
+ this._updateSessionActivity();
15230
13902
 
15231
- const { session } = getSession({
15232
- timeouts: this.timeouts,
15233
- stickySession: Boolean(this._options.stickySession),
15234
- currentSession: this.session,
15235
- // This is intentional: create a new session-based replay when calling `start()`
15236
- sessionSampleRate: 1,
15237
- allowBuffering: false,
13903
+ this.eventBuffer = createEventBuffer({
13904
+ useCompression: this._options.useCompression,
15238
13905
  });
15239
13906
 
15240
- session.previousSessionId = previousSessionId;
15241
- this.session = session;
15242
-
15243
- this._initializeRecording();
15244
- }
15245
-
15246
- /**
15247
- * Start replay buffering. Buffers until `flush()` is called or, if
15248
- * `replaysOnErrorSampleRate` > 0, an error occurs.
15249
- */
15250
- startBuffering() {
15251
- if (this._isEnabled) {
15252
- throw new Error('Replay recording is already in progress');
15253
- }
15254
-
15255
- const previousSessionId = this.session && this.session.id;
15256
-
15257
- const { session } = getSession({
15258
- timeouts: this.timeouts,
15259
- stickySession: Boolean(this._options.stickySession),
15260
- currentSession: this.session,
15261
- sessionSampleRate: 0,
15262
- allowBuffering: true,
15263
- });
13907
+ this._addListeners();
15264
13908
 
15265
- session.previousSessionId = previousSessionId;
15266
- this.session = session;
13909
+ // Need to set as enabled before we start recording, as `record()` can trigger a flush with a new checkout
13910
+ this._isEnabled = true;
15267
13911
 
15268
- this.recordingMode = 'buffer';
15269
- this._initializeRecording();
13912
+ this.startRecording();
15270
13913
  }
15271
13914
 
15272
13915
  /**
@@ -15281,7 +13924,7 @@ class ReplayContainer {
15281
13924
  // When running in error sampling mode, we need to overwrite `checkoutEveryNms`
15282
13925
  // Without this, it would record forever, until an error happens, which we don't want
15283
13926
  // instead, we'll always keep the last 60 seconds of replay before an error happened
15284
- ...(this.recordingMode === 'buffer' && { checkoutEveryNms: BUFFER_CHECKOUT_TIME }),
13927
+ ...(this.recordingMode === 'error' && { checkoutEveryNms: ERROR_CHECKOUT_TIME }),
15285
13928
  emit: getHandleRecordingEmit(this),
15286
13929
  onMutation: this._onMutationHandler,
15287
13930
  });
@@ -15292,18 +13935,17 @@ class ReplayContainer {
15292
13935
 
15293
13936
  /**
15294
13937
  * Stops the recording, if it was running.
15295
- *
15296
- * Returns true if it was previously stopped, or is now stopped,
15297
- * otherwise false.
13938
+ * Returns true if it was stopped, else false.
15298
13939
  */
15299
13940
  stopRecording() {
15300
13941
  try {
15301
13942
  if (this._stopRecording) {
15302
13943
  this._stopRecording();
15303
13944
  this._stopRecording = undefined;
13945
+ return true;
15304
13946
  }
15305
13947
 
15306
- return true;
13948
+ return false;
15307
13949
  } catch (err) {
15308
13950
  this._handleException(err);
15309
13951
  return false;
@@ -15314,7 +13956,7 @@ class ReplayContainer {
15314
13956
  * Currently, this needs to be manually called (e.g. for tests). Sentry SDK
15315
13957
  * does not support a teardown
15316
13958
  */
15317
- async stop(reason) {
13959
+ stop(reason) {
15318
13960
  if (!this._isEnabled) {
15319
13961
  return;
15320
13962
  }
@@ -15330,24 +13972,12 @@ class ReplayContainer {
15330
13972
  log(msg);
15331
13973
  }
15332
13974
 
15333
- // We can't move `_isEnabled` after awaiting a flush, otherwise we can
15334
- // enter into an infinite loop when `stop()` is called while flushing.
15335
13975
  this._isEnabled = false;
15336
13976
  this._removeListeners();
15337
13977
  this.stopRecording();
15338
-
15339
- this._debouncedFlush.cancel();
15340
- // See comment above re: `_isEnabled`, we "force" a flush, ignoring the
15341
- // `_isEnabled` state of the plugin since it was disabled above.
15342
- await this._flush({ force: true });
15343
-
15344
- // After flush, destroy event buffer
15345
13978
  this.eventBuffer && this.eventBuffer.destroy();
15346
13979
  this.eventBuffer = null;
15347
-
15348
- // Clear session from session storage, note this means if a new session
15349
- // is started after, it will not have `previousSessionId`
15350
- clearSession(this);
13980
+ this._debouncedFlush.cancel();
15351
13981
  } catch (err) {
15352
13982
  this._handleException(err);
15353
13983
  }
@@ -15378,45 +14008,6 @@ class ReplayContainer {
15378
14008
  this.startRecording();
15379
14009
  }
15380
14010
 
15381
- /**
15382
- * If not in "session" recording mode, flush event buffer which will create a new replay.
15383
- * Unless `continueRecording` is false, the replay will continue to record and
15384
- * behave as a "session"-based replay.
15385
- *
15386
- * Otherwise, queue up a flush.
15387
- */
15388
- async sendBufferedReplayOrFlush({ continueRecording = true } = {}) {
15389
- if (this.recordingMode === 'session') {
15390
- return this.flushImmediate();
15391
- }
15392
-
15393
- // Allow flush to complete before resuming as a session recording, otherwise
15394
- // the checkout from `startRecording` may be included in the payload.
15395
- // Prefer to keep the error replay as a separate (and smaller) segment
15396
- // than the session replay.
15397
- await this.flushImmediate();
15398
-
15399
- const hasStoppedRecording = this.stopRecording();
15400
-
15401
- if (!continueRecording || !hasStoppedRecording) {
15402
- return;
15403
- }
15404
-
15405
- // Re-start recording, but in "session" recording mode
15406
-
15407
- // Reset all "capture on error" configuration before
15408
- // starting a new recording
15409
- this.recordingMode = 'session';
15410
-
15411
- // Once this session ends, we do not want to refresh it
15412
- if (this.session) {
15413
- this.session.shouldRefresh = false;
15414
- this._maybeSaveSession();
15415
- }
15416
-
15417
- this.startRecording();
15418
- }
15419
-
15420
14011
  /**
15421
14012
  * We want to batch uploads of replay events. Save events only if
15422
14013
  * `<flushMinDelay>` milliseconds have elapsed since the last event
@@ -15426,12 +14017,12 @@ class ReplayContainer {
15426
14017
  * processing and hand back control to caller.
15427
14018
  */
15428
14019
  addUpdate(cb) {
15429
- // We need to always run `cb` (e.g. in the case of `this.recordingMode == 'buffer'`)
14020
+ // We need to always run `cb` (e.g. in the case of `this.recordingMode == 'error'`)
15430
14021
  const cbResult = cb();
15431
14022
 
15432
14023
  // If this option is turned on then we will only want to call `flush`
15433
14024
  // explicitly
15434
- if (this.recordingMode === 'buffer') {
14025
+ if (this.recordingMode === 'error') {
15435
14026
  return;
15436
14027
  }
15437
14028
 
@@ -15475,17 +14066,7 @@ class ReplayContainer {
15475
14066
  }
15476
14067
 
15477
14068
  /**
15478
- * Only flush if `this.recordingMode === 'session'`
15479
- */
15480
- conditionalFlush() {
15481
- if (this.recordingMode === 'buffer') {
15482
- return Promise.resolve();
15483
- }
15484
-
15485
- return this.flushImmediate();
15486
- }
15487
-
15488
- /**
14069
+ *
15489
14070
  * Always flush via `_debouncedFlush` so that we do not have flushes triggered
15490
14071
  * from calling both `flush` and `_debouncedFlush`. Otherwise, there could be
15491
14072
  * cases of mulitple flushes happening closely together.
@@ -15496,13 +14077,6 @@ class ReplayContainer {
15496
14077
  return this._debouncedFlush.flush() ;
15497
14078
  }
15498
14079
 
15499
- /**
15500
- * Cancels queued up flushes.
15501
- */
15502
- cancelFlush() {
15503
- this._debouncedFlush.cancel();
15504
- }
15505
-
15506
14080
  /** Get the current sesion (=replay) ID */
15507
14081
  getSessionId() {
15508
14082
  return this.session && this.session.id;
@@ -15520,12 +14094,12 @@ class ReplayContainer {
15520
14094
  const oldSessionId = this.getSessionId();
15521
14095
 
15522
14096
  // Prevent starting a new session if the last user activity is older than
15523
- // SESSION_IDLE_PAUSE_DURATION. Otherwise non-user activity can trigger a new
14097
+ // SESSION_IDLE_DURATION. Otherwise non-user activity can trigger a new
15524
14098
  // session+recording. This creates noisy replays that do not have much
15525
14099
  // content in them.
15526
14100
  if (
15527
14101
  this._lastActivity &&
15528
- isExpired(this._lastActivity, this.timeouts.sessionIdlePause) &&
14102
+ isExpired(this._lastActivity, this.timeouts.sessionIdle) &&
15529
14103
  this.session &&
15530
14104
  this.session.sampled === 'session'
15531
14105
  ) {
@@ -15575,30 +14149,6 @@ class ReplayContainer {
15575
14149
  this._context.urls.push(url);
15576
14150
  }
15577
14151
 
15578
- /**
15579
- * Initialize and start all listeners to varying events (DOM,
15580
- * Performance Observer, Recording, Sentry SDK, etc)
15581
- */
15582
- _initializeRecording() {
15583
- this.setInitialState();
15584
-
15585
- // this method is generally called on page load or manually - in both cases
15586
- // we should treat it as an activity
15587
- this._updateSessionActivity();
15588
-
15589
- this.eventBuffer = createEventBuffer({
15590
- useCompression: this._options.useCompression,
15591
- });
15592
-
15593
- this._removeListeners();
15594
- this._addListeners();
15595
-
15596
- // Need to set as enabled before we start recording, as `record()` can trigger a flush with a new checkout
15597
- this._isEnabled = true;
15598
-
15599
- this.startRecording();
15600
- }
15601
-
15602
14152
  /** A wrapper to conditionally capture exceptions. */
15603
14153
  _handleException(error) {
15604
14154
  (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.error('[Replay]', error);
@@ -15618,7 +14168,7 @@ class ReplayContainer {
15618
14168
  stickySession: Boolean(this._options.stickySession),
15619
14169
  currentSession: this.session,
15620
14170
  sessionSampleRate: this._options.sessionSampleRate,
15621
- allowBuffering: this._options.errorSampleRate > 0,
14171
+ errorSampleRate: this._options.errorSampleRate,
15622
14172
  });
15623
14173
 
15624
14174
  // If session was newly created (i.e. was not loaded from storage), then
@@ -15635,7 +14185,7 @@ class ReplayContainer {
15635
14185
  this.session = session;
15636
14186
 
15637
14187
  if (!this.session.sampled) {
15638
- void this.stop('session unsampled');
14188
+ this.stop('session unsampled');
15639
14189
  return false;
15640
14190
  }
15641
14191
 
@@ -15650,7 +14200,6 @@ class ReplayContainer {
15650
14200
  WINDOW.document.addEventListener('visibilitychange', this._handleVisibilityChange);
15651
14201
  WINDOW.addEventListener('blur', this._handleWindowBlur);
15652
14202
  WINDOW.addEventListener('focus', this._handleWindowFocus);
15653
- WINDOW.addEventListener('keydown', this._handleKeyboardEvent);
15654
14203
 
15655
14204
  // There is no way to remove these listeners, so ensure they are only added once
15656
14205
  if (!this._hasInitializedCoreListeners) {
@@ -15679,7 +14228,6 @@ class ReplayContainer {
15679
14228
 
15680
14229
  WINDOW.removeEventListener('blur', this._handleWindowBlur);
15681
14230
  WINDOW.removeEventListener('focus', this._handleWindowFocus);
15682
- WINDOW.removeEventListener('keydown', this._handleKeyboardEvent);
15683
14231
 
15684
14232
  if (this._performanceObserver) {
15685
14233
  this._performanceObserver.disconnect();
@@ -15730,11 +14278,6 @@ class ReplayContainer {
15730
14278
  this._doChangeToForegroundTasks(breadcrumb);
15731
14279
  };}
15732
14280
 
15733
- /** Ensure page remains active when a key is pressed. */
15734
- __init16() {this._handleKeyboardEvent = (event) => {
15735
- handleKeyboardEvent(this, event);
15736
- };}
15737
-
15738
14281
  /**
15739
14282
  * Tasks to run when we consider a page to be hidden (via blurring and/or visibility)
15740
14283
  */
@@ -15752,7 +14295,7 @@ class ReplayContainer {
15752
14295
  // Send replay when the page/tab becomes hidden. There is no reason to send
15753
14296
  // replay if it becomes visible, since no actions we care about were done
15754
14297
  // while it was hidden
15755
- void this.conditionalFlush();
14298
+ this._conditionalFlush();
15756
14299
  }
15757
14300
 
15758
14301
  /**
@@ -15766,7 +14309,7 @@ class ReplayContainer {
15766
14309
  const isSessionActive = this.checkAndHandleExpiredSession();
15767
14310
 
15768
14311
  if (!isSessionActive) {
15769
- // If the user has come back to the page within SESSION_IDLE_PAUSE_DURATION
14312
+ // If the user has come back to the page within SESSION_IDLE_DURATION
15770
14313
  // ms, we will re-use the existing session, otherwise create a new
15771
14314
  // session
15772
14315
  (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.log('[Replay] Document has become active, but session has expired');
@@ -15814,7 +14357,7 @@ class ReplayContainer {
15814
14357
  _createCustomBreadcrumb(breadcrumb) {
15815
14358
  this.addUpdate(() => {
15816
14359
  void addEvent(this, {
15817
- type: EventType$1.Custom,
14360
+ type: EventType.Custom,
15818
14361
  timestamp: breadcrumb.timestamp || 0,
15819
14362
  data: {
15820
14363
  tag: 'breadcrumb',
@@ -15836,6 +14379,17 @@ class ReplayContainer {
15836
14379
  return Promise.all(createPerformanceSpans(this, createPerformanceEntries(entries)));
15837
14380
  }
15838
14381
 
14382
+ /**
14383
+ * Only flush if `this.recordingMode === 'session'`
14384
+ */
14385
+ _conditionalFlush() {
14386
+ if (this.recordingMode === 'error') {
14387
+ return;
14388
+ }
14389
+
14390
+ void this.flushImmediate();
14391
+ }
14392
+
15839
14393
  /**
15840
14394
  * Clear _context
15841
14395
  */
@@ -15844,35 +14398,22 @@ class ReplayContainer {
15844
14398
  this._context.errorIds.clear();
15845
14399
  this._context.traceIds.clear();
15846
14400
  this._context.urls = [];
15847
- }
15848
-
15849
- /** Update the initial timestamp based on the buffer content. */
15850
- _updateInitialTimestampFromEventBuffer() {
15851
- const { session, eventBuffer } = this;
15852
- if (!session || !eventBuffer) {
15853
- return;
15854
- }
15855
-
15856
- // we only ever update this on the initial segment
15857
- if (session.segmentId) {
15858
- return;
15859
- }
15860
-
15861
- const earliestEvent = eventBuffer.getEarliestTimestamp();
15862
- if (earliestEvent && earliestEvent < this._context.initialTimestamp) {
15863
- this._context.initialTimestamp = earliestEvent;
15864
- }
14401
+ this._context.earliestEvent = null;
15865
14402
  }
15866
14403
 
15867
14404
  /**
15868
14405
  * Return and clear _context
15869
14406
  */
15870
14407
  _popEventContext() {
14408
+ if (this._context.earliestEvent && this._context.earliestEvent < this._context.initialTimestamp) {
14409
+ this._context.initialTimestamp = this._context.earliestEvent;
14410
+ }
14411
+
15871
14412
  const _context = {
15872
14413
  initialTimestamp: this._context.initialTimestamp,
15873
14414
  initialUrl: this._context.initialUrl,
15874
- errorIds: Array.from(this._context.errorIds),
15875
- traceIds: Array.from(this._context.traceIds),
14415
+ errorIds: Array.from(this._context.errorIds).filter(Boolean),
14416
+ traceIds: Array.from(this._context.traceIds).filter(Boolean),
15876
14417
  urls: this._context.urls,
15877
14418
  };
15878
14419
 
@@ -15911,9 +14452,6 @@ class ReplayContainer {
15911
14452
  }
15912
14453
 
15913
14454
  try {
15914
- // This uses the data from the eventBuffer, so we need to call this before `finish()
15915
- this._updateInitialTimestampFromEventBuffer();
15916
-
15917
14455
  // Note this empties the event buffer regardless of outcome of sending replay
15918
14456
  const recordingData = await this.eventBuffer.finish();
15919
14457
 
@@ -15929,6 +14467,7 @@ class ReplayContainer {
15929
14467
  replayId,
15930
14468
  recordingData,
15931
14469
  segmentId,
14470
+ includeReplayStartTimestamp: segmentId === 0,
15932
14471
  eventContext,
15933
14472
  session: this.session,
15934
14473
  options: this.getOptions(),
@@ -15940,7 +14479,7 @@ class ReplayContainer {
15940
14479
  // This means we retried 3 times and all of them failed,
15941
14480
  // or we ran into a problem we don't want to retry, like rate limiting.
15942
14481
  // In this case, we want to completely stop the replay - otherwise, we may get inconsistent segments
15943
- void this.stop('sendReplay');
14482
+ this.stop('sendReplay');
15944
14483
 
15945
14484
  const client = getCurrentHub().getClient();
15946
14485
 
@@ -15954,12 +14493,8 @@ class ReplayContainer {
15954
14493
  * Flush recording data to Sentry. Creates a lock so that only a single flush
15955
14494
  * can be active at a time. Do not call this directly.
15956
14495
  */
15957
- __init17() {this._flush = async ({
15958
- force = false,
15959
- }
15960
-
15961
- = {}) => {
15962
- if (!this._isEnabled && !force) {
14496
+ __init16() {this._flush = async () => {
14497
+ if (!this._isEnabled) {
15963
14498
  // This can happen if e.g. the replay was stopped because of exceeding the retry limit
15964
14499
  return;
15965
14500
  }
@@ -16009,7 +14544,7 @@ class ReplayContainer {
16009
14544
  }
16010
14545
 
16011
14546
  /** Handler for rrweb.record.onMutation */
16012
- __init18() {this._onMutationHandler = (mutations) => {
14547
+ __init17() {this._onMutationHandler = (mutations) => {
16013
14548
  const count = mutations.length;
16014
14549
 
16015
14550
  const mutationLimit = this._options._experiments.mutationLimit || 0;
@@ -16143,8 +14678,6 @@ function isElectronNodeRenderer() {
16143
14678
  const MEDIA_SELECTORS =
16144
14679
  'img,image,svg,video,object,picture,embed,map,audio,link[rel="icon"],link[rel="apple-touch-icon"]';
16145
14680
 
16146
- const DEFAULT_NETWORK_HEADERS = ['content-length', 'content-type', 'accept'];
16147
-
16148
14681
  let _initialized = false;
16149
14682
 
16150
14683
  /**
@@ -16185,11 +14718,6 @@ class Replay {
16185
14718
  maskAllInputs = true,
16186
14719
  blockAllMedia = true,
16187
14720
 
16188
- networkDetailAllowUrls = [],
16189
- networkCaptureBodies = true,
16190
- networkRequestHeaders = [],
16191
- networkResponseHeaders = [],
16192
-
16193
14721
  mask = [],
16194
14722
  unmask = [],
16195
14723
  block = [],
@@ -16197,8 +14725,6 @@ class Replay {
16197
14725
  ignore = [],
16198
14726
  maskFn,
16199
14727
 
16200
- beforeAddRecordingEvent,
16201
-
16202
14728
  // eslint-disable-next-line deprecation/deprecation
16203
14729
  blockClass,
16204
14730
  // eslint-disable-next-line deprecation/deprecation
@@ -16250,14 +14776,6 @@ class Replay {
16250
14776
  errorSampleRate,
16251
14777
  useCompression,
16252
14778
  blockAllMedia,
16253
- maskAllInputs,
16254
- maskAllText,
16255
- networkDetailAllowUrls,
16256
- networkCaptureBodies,
16257
- networkRequestHeaders: _getMergedNetworkHeaders(networkRequestHeaders),
16258
- networkResponseHeaders: _getMergedNetworkHeaders(networkResponseHeaders),
16259
- beforeAddRecordingEvent,
16260
-
16261
14779
  _experiments,
16262
14780
  };
16263
14781
 
@@ -16311,7 +14829,14 @@ Sentry.init({ replaysOnErrorSampleRate: ${errorSampleRate} })`,
16311
14829
  }
16312
14830
 
16313
14831
  /**
16314
- * Setup and initialize replay container
14832
+ * We previously used to create a transaction in `setupOnce` and it would
14833
+ * potentially create a transaction before some native SDK integrations have run
14834
+ * and applied their own global event processor. An example is:
14835
+ * https://github.com/getsentry/sentry-javascript/blob/b47ceafbdac7f8b99093ce6023726ad4687edc48/packages/browser/src/integrations/useragent.ts
14836
+ *
14837
+ * So we call `replay.setup` in next event loop as a workaround to wait for other
14838
+ * global event processors to finish. This is no longer needed, but keeping it
14839
+ * here to avoid any future issues.
16315
14840
  */
16316
14841
  setupOnce() {
16317
14842
  if (!isBrowser()) {
@@ -16320,20 +14845,12 @@ Sentry.init({ replaysOnErrorSampleRate: ${errorSampleRate} })`,
16320
14845
 
16321
14846
  this._setup();
16322
14847
 
16323
- // Once upon a time, we tried to create a transaction in `setupOnce` and it would
16324
- // potentially create a transaction before some native SDK integrations have run
16325
- // and applied their own global event processor. An example is:
16326
- // https://github.com/getsentry/sentry-javascript/blob/b47ceafbdac7f8b99093ce6023726ad4687edc48/packages/browser/src/integrations/useragent.ts
16327
- //
16328
- // So we call `this._initialize()` in next event loop as a workaround to wait for other
16329
- // global event processors to finish. This is no longer needed, but keeping it
16330
- // here to avoid any future issues.
16331
- setTimeout(() => this._initialize());
14848
+ // XXX: See method comments above
14849
+ setTimeout(() => this.start());
16332
14850
  }
16333
14851
 
16334
14852
  /**
16335
- * Start a replay regardless of sampling rate. Calling this will always
16336
- * create a new session. Will throw an error if replay is already in progress.
14853
+ * Initializes the plugin.
16337
14854
  *
16338
14855
  * Creates or loads a session, attaches listeners to varying events (DOM,
16339
14856
  * PerformanceObserver, Recording, Sentry SDK, etc)
@@ -16346,64 +14863,27 @@ Sentry.init({ replaysOnErrorSampleRate: ${errorSampleRate} })`,
16346
14863
  this._replay.start();
16347
14864
  }
16348
14865
 
16349
- /**
16350
- * Start replay buffering. Buffers until `flush()` is called or, if
16351
- * `replaysOnErrorSampleRate` > 0, until an error occurs.
16352
- */
16353
- startBuffering() {
16354
- if (!this._replay) {
16355
- return;
16356
- }
16357
-
16358
- this._replay.startBuffering();
16359
- }
16360
-
16361
14866
  /**
16362
14867
  * Currently, this needs to be manually called (e.g. for tests). Sentry SDK
16363
14868
  * does not support a teardown
16364
14869
  */
16365
14870
  stop() {
16366
14871
  if (!this._replay) {
16367
- return Promise.resolve();
16368
- }
16369
-
16370
- return this._replay.stop();
16371
- }
16372
-
16373
- /**
16374
- * If not in "session" recording mode, flush event buffer which will create a new replay.
16375
- * Unless `continueRecording` is false, the replay will continue to record and
16376
- * behave as a "session"-based replay.
16377
- *
16378
- * Otherwise, queue up a flush.
16379
- */
16380
- flush(options) {
16381
- if (!this._replay || !this._replay.isEnabled()) {
16382
- return Promise.resolve();
14872
+ return;
16383
14873
  }
16384
14874
 
16385
- return this._replay.sendBufferedReplayOrFlush(options);
14875
+ this._replay.stop();
16386
14876
  }
16387
14877
 
16388
14878
  /**
16389
- * Get the current session ID.
14879
+ * Immediately send all pending events.
16390
14880
  */
16391
- getReplayId() {
14881
+ flush() {
16392
14882
  if (!this._replay || !this._replay.isEnabled()) {
16393
14883
  return;
16394
14884
  }
16395
14885
 
16396
- return this._replay.getSessionId();
16397
- }
16398
- /**
16399
- * Initializes replay.
16400
- */
16401
- _initialize() {
16402
- if (!this._replay) {
16403
- return;
16404
- }
16405
-
16406
- this._replay.initializeSampling();
14886
+ return this._replay.flushImmediate();
16407
14887
  }
16408
14888
 
16409
14889
  /** Setup the integration. */
@@ -16454,10 +14934,6 @@ function loadReplayOptionsFromClient(initialOptions) {
16454
14934
  return finalOptions;
16455
14935
  }
16456
14936
 
16457
- function _getMergedNetworkHeaders(headers) {
16458
- return [...DEFAULT_NETWORK_HEADERS, ...headers.map(header => header.toLowerCase())];
16459
- }
16460
-
16461
14937
  /**
16462
14938
  * Polyfill for the optional chain operator, `?.`, given previous conversion of the expression into an array of values,
16463
14939
  * descriptors, and functions.
@@ -16895,9 +15371,6 @@ class Postgres {
16895
15371
  const span = _optionalChain([parentSpan, 'optionalAccess', _6 => _6.startChild, 'call', _7 => _7({
16896
15372
  description: typeof config === 'string' ? config : (config ).text,
16897
15373
  op: 'db',
16898
- data: {
16899
- 'db.system': 'postgresql',
16900
- },
16901
15374
  })]);
16902
15375
 
16903
15376
  if (typeof callback === 'function') {
@@ -16974,9 +15447,6 @@ class Mysql {constructor() { Mysql.prototype.__init.call(this); }
16974
15447
  const span = _optionalChain([parentSpan, 'optionalAccess', _4 => _4.startChild, 'call', _5 => _5({
16975
15448
  description: typeof options === 'string' ? options : (options ).sql,
16976
15449
  op: 'db',
16977
- data: {
16978
- 'db.system': 'mysql',
16979
- },
16980
15450
  })]);
16981
15451
 
16982
15452
  if (typeof callback === 'function') {
@@ -17198,7 +15668,6 @@ class Mongo {
17198
15668
  collectionName: collection.collectionName,
17199
15669
  dbName: collection.dbName,
17200
15670
  namespace: collection.namespace,
17201
- 'db.system': 'mongodb',
17202
15671
  };
17203
15672
  const spanContext = {
17204
15673
  op: 'db',
@@ -17285,15 +15754,31 @@ class Prisma {
17285
15754
  }
17286
15755
 
17287
15756
  this._client.$use((params, next) => {
15757
+ const scope = getCurrentHub().getScope();
15758
+ const parentSpan = _optionalChain([scope, 'optionalAccess', _2 => _2.getSpan, 'call', _3 => _3()]);
15759
+
17288
15760
  const action = params.action;
17289
15761
  const model = params.model;
17290
- return trace(
17291
- { name: model ? `${model} ${action}` : action, op: 'db.sql.prisma', data: { 'db.system': 'prisma' } },
17292
- () => next(params),
17293
- );
15762
+
15763
+ const span = _optionalChain([parentSpan, 'optionalAccess', _4 => _4.startChild, 'call', _5 => _5({
15764
+ description: model ? `${model} ${action}` : action,
15765
+ op: 'db.sql.prisma',
15766
+ })]);
15767
+
15768
+ const rv = next(params);
15769
+
15770
+ if (isThenable(rv)) {
15771
+ return rv.then((res) => {
15772
+ _optionalChain([span, 'optionalAccess', _6 => _6.finish, 'call', _7 => _7()]);
15773
+ return res;
15774
+ });
15775
+ }
15776
+
15777
+ _optionalChain([span, 'optionalAccess', _8 => _8.finish, 'call', _9 => _9()]);
15778
+ return rv;
17294
15779
  });
17295
15780
  }
17296
- } Prisma.__initStatic();
15781
+ }Prisma.__initStatic();
17297
15782
 
17298
15783
  /** Tracing integration for graphql package */
17299
15784
  class GraphQL {constructor() { GraphQL.prototype.__init.call(this); }
@@ -25625,6 +24110,7 @@ var STATES$5;
25625
24110
  STATES["RE_INIT_RECORDER__NEXT_QUESTION"] = "reInitRecorderNextQuestion";
25626
24111
  STATES["UPLOADING"] = "uploading";
25627
24112
  STATES["CONFIRM"] = "confirm";
24113
+ STATES["CONFIRM_WATING"] = "confirmWaiting";
25628
24114
  STATES["FINISHED"] = "finished";
25629
24115
  STATES["ERROR"] = "error";
25630
24116
  })(STATES$5 || (STATES$5 = {}));
@@ -25678,6 +24164,7 @@ var ACTIONS$6;
25678
24164
  ACTIONS["RESET_FAILED_RECORDING_ATTEMPTS"] = "resetFailedRecordingAttempts";
25679
24165
  ACTIONS["CLEAR_VIDEO_ERROR"] = "clearVideoError";
25680
24166
  ACTIONS["UPDATE_VIDEO_DIMENSIONS"] = "updateVideoDimensions";
24167
+ ACTIONS["UPDATE_UPLOADED_FALSE_COUNT"] = "updateUploadedFalseCount";
25681
24168
  })(ACTIONS$6 || (ACTIONS$6 = {}));
25682
24169
  var EVENTS$5;
25683
24170
  (function (EVENTS) {
@@ -25733,6 +24220,7 @@ var GUARDS$3;
25733
24220
  GUARDS["IS_RECORDER_READY"] = "isRecorderReady";
25734
24221
  GUARDS["IS_ASSESSMENT_QUESTION"] = "isAssessmentQuestion";
25735
24222
  GUARDS["IS_TIMES_UP"] = "isTimesUp";
24223
+ GUARDS["SHOULD_TRY_TO_CONFIRM"] = "shouldTryToConfirm";
25736
24224
  })(GUARDS$3 || (GUARDS$3 = {}));
25737
24225
  var TAGS;
25738
24226
  (function (TAGS) {
@@ -25742,6 +24230,7 @@ var TAGS;
25742
24230
  TAGS["DISPLAY_OUTER_VIEW"] = "displayOuterView";
25743
24231
  TAGS["DISPLAY_QUESTION"] = "displayQuestion";
25744
24232
  TAGS["DISPLAY_QUESTIONS_LIST"] = "displayQuestionsList";
24233
+ TAGS["DISPLAY_UPLOAD"] = "displayUpload";
25745
24234
  TAGS["LOADING"] = "loading";
25746
24235
  })(TAGS || (TAGS = {}));
25747
24236
 
@@ -30463,7 +28952,7 @@ const configGenerator = () => {
30463
28952
  let release;
30464
28953
  try {
30465
28954
  environment !== null && environment !== void 0 ? environment : (environment = "staging");
30466
- release !== null && release !== void 0 ? release : (release = "1.1.23-binary-check-one");
28955
+ release !== null && release !== void 0 ? release : (release = "1.1.23-binary-002");
30467
28956
  }
30468
28957
  catch (_a) {
30469
28958
  console.error('sentry configGenerator error');
@@ -30936,6 +29425,7 @@ const SECONDS_LEFT_HIGHLIGHT = 10;
30936
29425
  const DEFAULT_ASSESSMENT_MAX_CHARS = 300;
30937
29426
  const DEFAULT_ASSESSMENT_DURATION = 0;
30938
29427
  const DEFAULT_VIDEO_DIMENSIONS = { width: 1280, height: 720 }; // Transcoder default dimensions (720p)
29428
+ const MAX_CONFIRM_ATTEMPTS = 5;
30939
29429
  // Font
30940
29430
  const FONT_URL = 'https://fonts.googleapis.com/css2?family=Nunito+Sans:wght@400;600;700&display=swap';
30941
29431
  var RETAKE_SPEED;
@@ -35096,8 +33586,8 @@ function memoizeOne(resultFn, isEqual) {
35096
33586
  }
35097
33587
 
35098
33588
  var memoizeOne_esm = /*#__PURE__*/Object.freeze({
35099
- __proto__: null,
35100
- 'default': memoizeOne
33589
+ __proto__: null,
33590
+ 'default': memoizeOne
35101
33591
  });
35102
33592
 
35103
33593
  var require$$2 = /*@__PURE__*/getAugmentedNamespace(memoizeOne_esm);
@@ -39057,6 +37547,11 @@ var EVENT_TYPES;
39057
37547
  EVENT_TYPES["SOUND_RESTORED"] = "soundRestored";
39058
37548
  EVENT_TYPES["TIMES_UP"] = "timesUp";
39059
37549
  EVENT_TYPES["COMPLETED_INTERVIEW"] = "completedInterview";
37550
+ EVENT_TYPES["RECONNECTED"] = "reconnected";
37551
+ EVENT_TYPES["CONFIRM_UPLOADED_FAILED"] = "confirmUploadedFailed";
37552
+ EVENT_TYPES["FINISHED"] = "finished";
37553
+ EVENT_TYPES["ON_FINISH_SUCCEED"] = "onFinishSucceed";
37554
+ EVENT_TYPES["ON_FINISH_FAILED"] = "onFinishFailed";
39060
37555
  })(EVENT_TYPES || (EVENT_TYPES = {}));
39061
37556
  let event_id;
39062
37557
  const updateEventId = (eventId) => { event_id = eventId; };
@@ -39606,31 +38101,6 @@ const Setup = ({ widgetMachine, sendToWidget, isPracticeDisabled, recordWithoutV
39606
38101
  React__default.createElement(C, { className: startButtonClassNames, color: "special", backgroundColor: "white", onClick: () => sendToWidget(EVENTS$5.QUESTION_MODE), disabled: !canStartInterview }, t(isResumed ? 'welcome.resumeInterview' : 'buttons.btn_start').toUpperCase()))));
39607
38102
  };
39608
38103
 
39609
- /*! *****************************************************************************
39610
- Copyright (c) Microsoft Corporation.
39611
-
39612
- Permission to use, copy, modify, and/or distribute this software for any
39613
- purpose with or without fee is hereby granted.
39614
-
39615
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
39616
- REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
39617
- AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
39618
- INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
39619
- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
39620
- OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
39621
- PERFORMANCE OF THIS SOFTWARE.
39622
- ***************************************************************************** */
39623
-
39624
- function __awaiter(thisArg, _arguments, P, generator) {
39625
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
39626
- return new (P || (P = Promise))(function (resolve, reject) {
39627
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
39628
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
39629
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
39630
- step((generator = generator.apply(thisArg, _arguments || [])).next());
39631
- });
39632
- }
39633
-
39634
38104
  const uploadToS3 = (url, data, onProgress, timeout = 0) => s3AxiosInstance.put(url, data, {
39635
38105
  headers: { 'Content-Type': 'video/webm' }, onUploadProgress: onProgress, timeout,
39636
38106
  });
@@ -39701,8 +38171,8 @@ const OuterView = ({ widgetMachine, sendToWidget, recorderRef }) => {
39701
38171
  const isQuestionDisplayed = widgetMachine.hasTag(TAGS.DISPLAY_QUESTION) || (isVideoQuestionState && !currentQuestionObj.thinkingTime);
39702
38172
  const isPreviewState = widgetMachine.matches(STATES$5.PREVIEW);
39703
38173
  const isUploadingState = widgetMachine.matches(STATES$5.UPLOADING);
39704
- const isConfirmState = widgetMachine.matches(STATES$5.CONFIRM);
39705
38174
  const isQuestionsListDisplayed = widgetMachine.hasTag(TAGS.DISPLAY_QUESTIONS_LIST);
38175
+ const isUploadDisplayed = widgetMachine.hasTag(TAGS.DISPLAY_UPLOAD);
39706
38176
  const isRecording = recorderMachine.matches({ [STATES$6.RECORDING]: STATES$6.COLLECTING_BLOBS });
39707
38177
  const isCountDown = recorderMachine.matches({ [STATES$6.RECORDING]: STATES$6.COUNT_DOWN });
39708
38178
  const isPracticeMode = recordingType === TAKE_TYPES.PRACTICE;
@@ -39720,7 +38190,7 @@ const OuterView = ({ widgetMachine, sendToWidget, recorderRef }) => {
39720
38190
  isSetupState && React__default.createElement(Setup, { recordWithoutVideo: recordWithoutVideo, widgetMachine: widgetMachine, sendToWidget: sendToWidget, isPracticeDisabled: !!config.disablePractice }),
39721
38191
  isQuestionsListDisplayed && React__default.createElement(QuestionsList, { questions: questions, currentQuestion: currentQuestion, isPracticeMode: isPracticeMode, questionsStatus: questionsStatus }),
39722
38192
  isQuestionDisplayed && React__default.createElement(Question, { questionObj: currentQuestionObj }),
39723
- (isUploadingState || isConfirmState) && (React__default.createElement(Upload, { isConnected: isConnected, totalFileSize: totalFileSize, totalUploadedFilesSize: totalUploadedFilesSize, totalUploadSpeed: totalUploadSpeed }))));
38193
+ isUploadDisplayed && (React__default.createElement(Upload, { isConnected: isConnected, totalFileSize: totalFileSize, totalUploadedFilesSize: totalUploadedFilesSize, totalUploadSpeed: totalUploadSpeed }))));
39724
38194
  };
39725
38195
 
39726
38196
  var actions = {};
@@ -43549,9 +42019,7 @@ var AsapAction = (function (_super) {
43549
42019
  var actions = scheduler.actions;
43550
42020
  if (id != null && ((_a = actions[actions.length - 1]) === null || _a === void 0 ? void 0 : _a.id) !== id) {
43551
42021
  immediateProvider.clearImmediate(id);
43552
- if (scheduler._scheduled === id) {
43553
- scheduler._scheduled = undefined;
43554
- }
42022
+ scheduler._scheduled = undefined;
43555
42023
  }
43556
42024
  return undefined;
43557
42025
  };
@@ -44817,7 +43285,7 @@ const accUploaderMachine = createMachine({
44817
43285
  actions: [ACTIONS$2.SENTRY],
44818
43286
  },
44819
43287
  {
44820
- actions: [ACTIONS$2.SET_UPLOADED, ACTIONS$2.SENTRY],
43288
+ actions: [ACTIONS$2.SENTRY],
44821
43289
  target: `#uploader.${STATES$2.UPLOADED}`, // In case the video already uploaded
44822
43290
  },
44823
43291
  ],
@@ -44834,7 +43302,6 @@ const accUploaderMachine = createMachine({
44834
43302
  id: 'uploadToS3',
44835
43303
  src: ({ signedUrl, file }) => (callback) => uploadToS3(signedUrl, file, callback),
44836
43304
  onDone: {
44837
- // actions: [ACTIONS.SET_UPLOADED],
44838
43305
  target: `#uploader.${STATES$2.UPLOADED}`,
44839
43306
  },
44840
43307
  onError: {
@@ -45190,10 +43657,14 @@ const accWidgetMachine = createMachine({
45190
43657
  failedRecordingMessage: VIDEO_CORRUPTED_STATES.NO_ERROR,
45191
43658
  isResumed: false,
45192
43659
  videoDimensions: DEFAULT_VIDEO_DIMENSIONS,
43660
+ confirmUploadedFalseCount: 0,
45193
43661
  },
45194
43662
  on: {
45195
43663
  [EVENTS$1.CONNECTION_CHANGED]: {
45196
- actions: [ACTIONS$6.SET_CONNECTION],
43664
+ actions: [
43665
+ ACTIONS$6.SET_CONNECTION,
43666
+ { type: ACTIONS$6.EMIT_TRACKING_EVENT, data: { eventType: EVENT_TYPES.RECONNECTED } },
43667
+ ],
45197
43668
  },
45198
43669
  [EVENTS$5.UPLOADER_PROGRESS]: {
45199
43670
  actions: [ACTIONS$6.UPDATE_TOTAL_UPLOADED_FILES_SIZE],
@@ -45720,7 +44191,7 @@ const accWidgetMachine = createMachine({
45720
44191
  },
45721
44192
  },
45722
44193
  [STATES$5.UPLOADING]: {
45723
- tags: [TAGS.DISPLAY_OUTER_VIEW],
44194
+ tags: [TAGS.DISPLAY_OUTER_VIEW, TAGS.DISPLAY_UPLOAD],
45724
44195
  entry: [
45725
44196
  { type: ACTIONS$6.SESSION_EVENT, data: { event: 'widget_completed', type: 'date' } },
45726
44197
  { type: ACTIONS$6.CONSOLE_DEBUG, data: { message: DEBUG.UPLOADING_STATE } },
@@ -45734,30 +44205,57 @@ const accWidgetMachine = createMachine({
45734
44205
  },
45735
44206
  },
45736
44207
  },
44208
+ [STATES$5.CONFIRM_WATING]: {
44209
+ tags: [TAGS.DISPLAY_OUTER_VIEW, TAGS.DISPLAY_UPLOAD],
44210
+ always: {
44211
+ target: STATES$5.CONFIRM,
44212
+ cond: GUARDS$3.IS_CONNECTED,
44213
+ },
44214
+ },
45737
44215
  [STATES$5.CONFIRM]: {
45738
- tags: [TAGS.DISPLAY_OUTER_VIEW],
44216
+ tags: [TAGS.DISPLAY_OUTER_VIEW, TAGS.DISPLAY_UPLOAD],
45739
44217
  invoke: {
45740
44218
  id: 'getVideo',
45741
44219
  src: (context) => { var _a; return getVideo(((_a = context.widgetConfig.video) === null || _a === void 0 ? void 0 : _a.video_id) || ''); },
45742
- onDone: {
45743
- target: STATES$5.FINISHED,
45744
- cond: (_, event) => !!event.data.data.data.video.uploaded,
45745
- },
44220
+ onDone: [
44221
+ {
44222
+ target: STATES$5.FINISHED,
44223
+ cond: (_, event) => !!event.data.data.data.video.uploaded,
44224
+ },
44225
+ {
44226
+ actions: [
44227
+ ACTIONS$6.UPDATE_UPLOADED_FALSE_COUNT,
44228
+ { type: ACTIONS$6.SENTRY, data: { eventName: 'CONFIRM::UPLOADED_FALSE' } },
44229
+ ],
44230
+ target: STATES$5.CONFIRM_WATING,
44231
+ cond: GUARDS$3.SHOULD_TRY_TO_CONFIRM,
44232
+ },
44233
+ {
44234
+ actions: [
44235
+ { type: ACTIONS$6.SET_VIDEO_ERROR, data: { errorMessage: 'Error, Please contact support' } },
44236
+ { type: ACTIONS$6.EMIT_TRACKING_EVENT, data: { eventType: EVENT_TYPES.CONFIRM_UPLOADED_FAILED } },
44237
+ ],
44238
+ target: STATES$5.ERROR,
44239
+ },
44240
+ ],
45746
44241
  onError: [
45747
44242
  {
45748
- actions: [() => console.error('UPDATE_VIDEO_UPLADED_ERROR:'), console.error, ACTIONS$6.SENTRY],
44243
+ actions: [() => console.error('UPDATE_VIDEO_UPLADED_ERROR:'), console.error, { type: ACTIONS$6.SENTRY, data: { eventName: 'CONFIRM::ERROR__NOT_FOUND' } }],
45749
44244
  target: STATES$5.FINISHED,
45750
44245
  cond: (_, event) => event.data.response.status === STATUS_CODES.NOT_FOUND,
45751
44246
  },
45752
44247
  {
45753
- actions: [ACTIONS$6.SENTRY],
45754
- target: STATES$5.CONFIRM,
44248
+ actions: [() => console.error('UPDATE_VIDEO_UPLADED_ERROR:'), console.error, { type: ACTIONS$6.SENTRY, data: { eventName: 'CONFIRM::ERROR' } }],
44249
+ target: STATES$5.CONFIRM_WATING,
45755
44250
  },
45756
44251
  ],
45757
44252
  },
45758
44253
  },
45759
44254
  [STATES$5.FINISHED]: {
45760
- entry: [{ type: ACTIONS$6.CONSOLE_DEBUG, data: { message: DEBUG.INTERVIEW_SUBMITTED } }],
44255
+ entry: [
44256
+ { type: ACTIONS$6.CONSOLE_DEBUG, data: { message: DEBUG.INTERVIEW_SUBMITTED } },
44257
+ { type: ACTIONS$6.EMIT_TRACKING_EVENT, data: { eventType: EVENT_TYPES.FINISHED } },
44258
+ ],
45761
44259
  type: 'final',
45762
44260
  },
45763
44261
  [STATES$5.ERROR]: {
@@ -45953,9 +44451,12 @@ const accWidgetMachine = createMachine({
45953
44451
  widgetConfig: Object.assign(Object.assign({}, widgetConfig), { video: Object.assign(Object.assign({}, widgetConfig.video), { videos: (_b = (_a = widgetConfig.video) === null || _a === void 0 ? void 0 : _a.videos) === null || _b === void 0 ? void 0 : _b.map((video, idx) => ((idx !== questionNumber - 1) ? video : videoFile)) }) }),
45954
44452
  });
45955
44453
  }),
45956
- [ACTIONS$6.SET_VIDEO_ERROR]: assign$2((_, event) => ({
45957
- error: event.data.response.data || { errorMessage: event.data.message },
45958
- })),
44454
+ [ACTIONS$6.SET_VIDEO_ERROR]: assign$2((_, event, meta) => {
44455
+ var _a, _b, _c, _d;
44456
+ return ({
44457
+ error: ((_b = (_a = event.data) === null || _a === void 0 ? void 0 : _a.response) === null || _b === void 0 ? void 0 : _b.data) || { errorMessage: ((_c = event.data) === null || _c === void 0 ? void 0 : _c.message) || ((_d = meta.action.data) === null || _d === void 0 ? void 0 : _d.errorMessage) },
44458
+ });
44459
+ }),
45959
44460
  [ACTIONS$6.REVOKE_MEMORY]: send$2((_, event) => ({ type: EVENTS$4.REMOVE_TAKES, data: { questionToRemove: event.data.questionNumber } }), { to: ({ storageRef }) => storageRef }),
45960
44461
  [ACTIONS$6.UPDATE_VIDEO_OBJECT]: assign$2(({ widgetConfig, recordingType }) => {
45961
44462
  var _a, _b;
@@ -45989,6 +44490,9 @@ const accWidgetMachine = createMachine({
45989
44490
  height: event.data.height,
45990
44491
  },
45991
44492
  })),
44493
+ [ACTIONS$6.UPDATE_UPLOADED_FALSE_COUNT]: assign$2(({ confirmUploadedFalseCount }) => ({
44494
+ confirmUploadedFalseCount: confirmUploadedFalseCount + 1,
44495
+ })),
45992
44496
  },
45993
44497
  services: {
45994
44498
  [SERVICES$1.UPDATE_VIDEO_OBJECT_CALL]: ({ widgetConfig: { video }, recordingType, speedTestResult }, event, meta) => (callback, onReceive) => {
@@ -46032,6 +44536,7 @@ const accWidgetMachine = createMachine({
46032
44536
  [GUARDS$3.IS_RECORDER_READY]: ({ recorderRef }) => (recorderRef === null || recorderRef === void 0 ? void 0 : recorderRef.getSnapshot().value) === STATES$6.IDLE,
46033
44537
  [GUARDS$3.IS_ASSESSMENT_QUESTION]: ({ questions, currentQuestion }) => !!questions[currentQuestion - 1].answerType && questions[currentQuestion - 1].answerType !== ANSWER_TYPES.VIDEO,
46034
44538
  [GUARDS$3.IS_TIMES_UP]: (_, event) => !!event.data.isTimesUp,
44539
+ [GUARDS$3.SHOULD_TRY_TO_CONFIRM]: ({ confirmUploadedFalseCount }) => confirmUploadedFalseCount <= MAX_CONFIRM_ATTEMPTS,
46035
44540
  },
46036
44541
  });
46037
44542
 
@@ -48554,10 +47059,25 @@ const Main = ({ widgetConfig, setShouldShowWaterMark, myinterviewRef, isWidgetMi
48554
47059
  useEffect(() => {
48555
47060
  setShouldShowWaterMark(!!(company === null || company === void 0 ? void 0 : company.shouldShowWaterMark));
48556
47061
  }, [company === null || company === void 0 ? void 0 : company.shouldShowWaterMark]);
47062
+ const _onFinish = () => __awaiter(void 0, void 0, void 0, function* () { var _a, _b; return (_b = (_a = widgetConfig.config).onFinish) === null || _b === void 0 ? void 0 : _b.call(_a, { redirectUrl: candidate.redirectUrl, video_id: (video === null || video === void 0 ? void 0 : video.video_id) || '' }); });
48557
47063
  useEffect(() => {
48558
- var _a, _b;
48559
47064
  if (machine.done) {
48560
- (_b = (_a = widgetConfig.config).onFinish) === null || _b === void 0 ? void 0 : _b.call(_a, { redirectUrl: candidate.redirectUrl, video_id: (video === null || video === void 0 ? void 0 : video.video_id) || '' });
47065
+ if (widgetConfig.config.onFinish) {
47066
+ _onFinish().then(() => {
47067
+ emitTrackEvent({ eventType: EVENT_TYPES.ON_FINISH_SUCCEED });
47068
+ }).catch((err) => {
47069
+ var _a;
47070
+ let errorMessage = '';
47071
+ try {
47072
+ errorMessage = (err === null || err === void 0 ? void 0 : err.message) || (err === null || err === void 0 ? void 0 : err.msg) || ((_a = err === null || err === void 0 ? void 0 : err.data) === null || _a === void 0 ? void 0 : _a.message);
47073
+ }
47074
+ catch (_b) {
47075
+ //
47076
+ }
47077
+ emitTrackEvent({ eventType: EVENT_TYPES.ON_FINISH_FAILED, extraData: { errorMessage } });
47078
+ throw err;
47079
+ });
47080
+ }
48561
47081
  }
48562
47082
  }, [machine.done]);
48563
47083
  useEffect(() => {
@@ -48573,11 +47093,16 @@ const Main = ({ widgetConfig, setShouldShowWaterMark, myinterviewRef, isWidgetMi
48573
47093
  });
48574
47094
  }
48575
47095
  }, [candidate, job === null || job === void 0 ? void 0 : job.language]);
47096
+ const setBackgroundOpacity = () => {
47097
+ var _a;
47098
+ (_a = myinterviewRef === null || myinterviewRef === void 0 ? void 0 : myinterviewRef.current) === null || _a === void 0 ? void 0 : _a.style.setProperty('--myinterview-background-opacity', isLoading || isErrorState ? '0' : '1');
47099
+ };
48576
47100
  useEffect(() => {
48577
47101
  var _a, _b;
48578
47102
  if (isErrorState && (error === null || error === void 0 ? void 0 : error.statusCode) === 400) {
48579
47103
  (_b = (_a = widgetConfig.config).onError) === null || _b === void 0 ? void 0 : _b.call(_a, { messageType: 'applied' });
48580
47104
  }
47105
+ setBackgroundOpacity();
48581
47106
  }, [isErrorState]);
48582
47107
  const isResumed = useMemo(() => { var _a, _b; return !!((_b = (_a = widgetConfig.video) === null || _a === void 0 ? void 0 : _a.videos) === null || _b === void 0 ? void 0 : _b.some((v) => v.uploaded)); }, [widgetConfig.video]);
48583
47108
  const handleScroll = (e) => {
@@ -48587,8 +47112,7 @@ const Main = ({ widgetConfig, setShouldShowWaterMark, myinterviewRef, isWidgetMi
48587
47112
  (_b = viewsRef === null || viewsRef === void 0 ? void 0 : viewsRef.current) === null || _b === void 0 ? void 0 : _b.style.setProperty('--myinterview-widget-practice-opacity', scrollTop > 10 ? '0' : '1');
48588
47113
  };
48589
47114
  useEffect(() => {
48590
- var _a;
48591
- (_a = myinterviewRef === null || myinterviewRef === void 0 ? void 0 : myinterviewRef.current) === null || _a === void 0 ? void 0 : _a.style.setProperty('--myinterview-background-opacity', isLoading || isErrorState ? '0' : '1');
47115
+ setBackgroundOpacity();
48592
47116
  }, [isLoading]);
48593
47117
  const onRetry = () => {
48594
47118
  send(EVENTS$5.RETRY);
@@ -48786,15 +47310,15 @@ const Widget = ({ candidate, job, video, config, disabled = false, buttonText =
48786
47310
  revertBodyStyling();
48787
47311
  setIsWidgetOpen(false);
48788
47312
  };
48789
- const onInterviewCompleted = (data) => {
47313
+ const onInterviewCompleted = (data) => __awaiter(void 0, void 0, void 0, function* () {
48790
47314
  var _a;
48791
- (_a = config.onFinish) === null || _a === void 0 ? void 0 : _a.call(config, data);
48792
47315
  onCloseWidget();
48793
- };
47316
+ yield ((_a = config.onFinish) === null || _a === void 0 ? void 0 : _a.call(config, data));
47317
+ });
48794
47318
  const onError = (data) => {
48795
47319
  var _a;
48796
- (_a = config.onError) === null || _a === void 0 ? void 0 : _a.call(config, data);
48797
47320
  onCloseWidget();
47321
+ (_a = config.onError) === null || _a === void 0 ? void 0 : _a.call(config, data);
48798
47322
  };
48799
47323
  const openWidget = () => {
48800
47324
  var _a;