@myinterview/widget-react 1.1.23-binary-004 → 1.1.23-binary-006

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/cjs/index.js CHANGED
@@ -973,12 +973,6 @@ function createStackParser(...parsers) {
973
973
  // Remove webpack (error: *) wrappers
974
974
  const cleanedLine = WEBPACK_ERROR_REGEXP.test(line) ? line.replace(WEBPACK_ERROR_REGEXP, '$1') : line;
975
975
 
976
- // https://github.com/getsentry/sentry-javascript/issues/7813
977
- // Skip Error: lines
978
- if (cleanedLine.match(/\S*Error: /)) {
979
- continue;
980
- }
981
-
982
976
  for (const parser of sortedParsers) {
983
977
  const frame = parser(cleanedLine);
984
978
 
@@ -1163,8 +1157,6 @@ function supportsHistory() {
1163
1157
  // eslint-disable-next-line deprecation/deprecation
1164
1158
  const WINDOW$3 = getGlobalObject();
1165
1159
 
1166
- const SENTRY_XHR_DATA_KEY = '__sentry_xhr_v2__';
1167
-
1168
1160
  /**
1169
1161
  * Instrument native APIs to call handlers that can be used to create breadcrumbs, APM spans etc.
1170
1162
  * - Console API
@@ -1277,13 +1269,11 @@ function instrumentFetch() {
1277
1269
 
1278
1270
  fill(WINDOW$3, 'fetch', function (originalFetch) {
1279
1271
  return function (...args) {
1280
- const { method, url } = parseFetchArgs(args);
1281
-
1282
1272
  const handlerData = {
1283
1273
  args,
1284
1274
  fetchData: {
1285
- method,
1286
- url,
1275
+ method: getFetchMethod(args),
1276
+ url: getFetchUrl(args),
1287
1277
  },
1288
1278
  startTimestamp: Date.now(),
1289
1279
  };
@@ -1318,53 +1308,29 @@ function instrumentFetch() {
1318
1308
  });
1319
1309
  }
1320
1310
 
1321
- function hasProp(obj, prop) {
1322
- return !!obj && typeof obj === 'object' && !!(obj )[prop];
1323
- }
1324
-
1325
- function getUrlFromResource(resource) {
1326
- if (typeof resource === 'string') {
1327
- return resource;
1328
- }
1329
-
1330
- if (!resource) {
1331
- return '';
1332
- }
1333
-
1334
- if (hasProp(resource, 'url')) {
1335
- return resource.url;
1311
+ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
1312
+ /** Extract `method` from fetch call arguments */
1313
+ function getFetchMethod(fetchArgs = []) {
1314
+ if ('Request' in WINDOW$3 && isInstanceOf(fetchArgs[0], Request) && fetchArgs[0].method) {
1315
+ return String(fetchArgs[0].method).toUpperCase();
1336
1316
  }
1337
-
1338
- if (resource.toString) {
1339
- return resource.toString();
1317
+ if (fetchArgs[1] && fetchArgs[1].method) {
1318
+ return String(fetchArgs[1].method).toUpperCase();
1340
1319
  }
1341
-
1342
- return '';
1320
+ return 'GET';
1343
1321
  }
1344
1322
 
1345
- /**
1346
- * Parses the fetch arguments to find the used Http method and the url of the request
1347
- */
1348
- function parseFetchArgs(fetchArgs) {
1349
- if (fetchArgs.length === 0) {
1350
- return { method: 'GET', url: '' };
1323
+ /** Extract `url` from fetch call arguments */
1324
+ function getFetchUrl(fetchArgs = []) {
1325
+ if (typeof fetchArgs[0] === 'string') {
1326
+ return fetchArgs[0];
1351
1327
  }
1352
-
1353
- if (fetchArgs.length === 2) {
1354
- const [url, options] = fetchArgs ;
1355
-
1356
- return {
1357
- url: getUrlFromResource(url),
1358
- method: hasProp(options, 'method') ? String(options.method).toUpperCase() : 'GET',
1359
- };
1328
+ if ('Request' in WINDOW$3 && isInstanceOf(fetchArgs[0], Request)) {
1329
+ return fetchArgs[0].url;
1360
1330
  }
1361
-
1362
- const arg = fetchArgs[0];
1363
- return {
1364
- url: getUrlFromResource(arg ),
1365
- method: hasProp(arg, 'method') ? String(arg.method).toUpperCase() : 'GET',
1366
- };
1331
+ return String(fetchArgs[0]);
1367
1332
  }
1333
+ /* eslint-enable @typescript-eslint/no-unsafe-member-access */
1368
1334
 
1369
1335
  /** JSDoc */
1370
1336
  function instrumentXHR() {
@@ -1377,11 +1343,10 @@ function instrumentXHR() {
1377
1343
  fill(xhrproto, 'open', function (originalOpen) {
1378
1344
  return function ( ...args) {
1379
1345
  const url = args[1];
1380
- const xhrInfo = (this[SENTRY_XHR_DATA_KEY] = {
1346
+ const xhrInfo = (this.__sentry_xhr__ = {
1381
1347
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
1382
1348
  method: isString$2(args[0]) ? args[0].toUpperCase() : args[0],
1383
1349
  url: args[1],
1384
- request_headers: {},
1385
1350
  });
1386
1351
 
1387
1352
  // if Sentry key appears in URL, don't capture it as a request
@@ -1392,7 +1357,7 @@ function instrumentXHR() {
1392
1357
 
1393
1358
  const onreadystatechangeHandler = () => {
1394
1359
  // For whatever reason, this is not the same instance here as from the outer method
1395
- const xhrInfo = this[SENTRY_XHR_DATA_KEY];
1360
+ const xhrInfo = this.__sentry_xhr__;
1396
1361
 
1397
1362
  if (!xhrInfo) {
1398
1363
  return;
@@ -1427,32 +1392,14 @@ function instrumentXHR() {
1427
1392
  this.addEventListener('readystatechange', onreadystatechangeHandler);
1428
1393
  }
1429
1394
 
1430
- // Intercepting `setRequestHeader` to access the request headers of XHR instance.
1431
- // This will only work for user/library defined headers, not for the default/browser-assigned headers.
1432
- // Request cookies are also unavailable for XHR, as `Cookie` header can't be defined by `setRequestHeader`.
1433
- fill(this, 'setRequestHeader', function (original) {
1434
- return function ( ...setRequestHeaderArgs) {
1435
- const [header, value] = setRequestHeaderArgs ;
1436
-
1437
- const xhrInfo = this[SENTRY_XHR_DATA_KEY];
1438
-
1439
- if (xhrInfo) {
1440
- xhrInfo.request_headers[header.toLowerCase()] = value;
1441
- }
1442
-
1443
- return original.apply(this, setRequestHeaderArgs);
1444
- };
1445
- });
1446
-
1447
1395
  return originalOpen.apply(this, args);
1448
1396
  };
1449
1397
  });
1450
1398
 
1451
1399
  fill(xhrproto, 'send', function (originalSend) {
1452
1400
  return function ( ...args) {
1453
- const sentryXhrData = this[SENTRY_XHR_DATA_KEY];
1454
- if (sentryXhrData && args[0] !== undefined) {
1455
- sentryXhrData.body = args[0];
1401
+ if (this.__sentry_xhr__ && args[0] !== undefined) {
1402
+ this.__sentry_xhr__.body = args[0];
1456
1403
  }
1457
1404
 
1458
1405
  triggerHandlers('xhr', {
@@ -1751,15 +1698,13 @@ function instrumentError() {
1751
1698
  url,
1752
1699
  });
1753
1700
 
1754
- if (_oldOnErrorHandler && !_oldOnErrorHandler.__SENTRY_LOADER__) {
1701
+ if (_oldOnErrorHandler) {
1755
1702
  // eslint-disable-next-line prefer-rest-params
1756
1703
  return _oldOnErrorHandler.apply(this, arguments);
1757
1704
  }
1758
1705
 
1759
1706
  return false;
1760
1707
  };
1761
-
1762
- WINDOW$3.onerror.__SENTRY_INSTRUMENTED__ = true;
1763
1708
  }
1764
1709
 
1765
1710
  let _oldOnUnhandledRejectionHandler = null;
@@ -1770,15 +1715,13 @@ function instrumentUnhandledRejection() {
1770
1715
  WINDOW$3.onunhandledrejection = function (e) {
1771
1716
  triggerHandlers('unhandledrejection', e);
1772
1717
 
1773
- if (_oldOnUnhandledRejectionHandler && !_oldOnUnhandledRejectionHandler.__SENTRY_LOADER__) {
1718
+ if (_oldOnUnhandledRejectionHandler) {
1774
1719
  // eslint-disable-next-line prefer-rest-params
1775
1720
  return _oldOnUnhandledRejectionHandler.apply(this, arguments);
1776
1721
  }
1777
1722
 
1778
1723
  return true;
1779
1724
  };
1780
-
1781
- WINDOW$3.onunhandledrejection.__SENTRY_INSTRUMENTED__ = true;
1782
1725
  }
1783
1726
 
1784
1727
  /* eslint-disable @typescript-eslint/no-unsafe-member-access */
@@ -2076,7 +2019,7 @@ function loadModule(moduleName) {
2076
2019
  * @returns A normalized version of the object, or `"**non-serializable**"` if any errors are thrown during normalization.
2077
2020
  */
2078
2021
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
2079
- function normalize(input, depth = 100, maxProperties = +Infinity) {
2022
+ function normalize(input, depth = +Infinity, maxProperties = +Infinity) {
2080
2023
  try {
2081
2024
  // since we're at the outermost level, we don't provide a key
2082
2025
  return visit('', input, depth, maxProperties);
@@ -2122,10 +2065,7 @@ function visit(
2122
2065
  const [memoize, unmemoize] = memo;
2123
2066
 
2124
2067
  // Get the simple cases out of the way first
2125
- if (
2126
- value == null || // this matches null and undefined -> eqeq not eqeqeq
2127
- (['number', 'boolean', 'string'].includes(typeof value) && !isNaN$1(value))
2128
- ) {
2068
+ if (value === null || (['number', 'boolean', 'string'].includes(typeof value) && !isNaN$1(value))) {
2129
2069
  return value ;
2130
2070
  }
2131
2071
 
@@ -2146,16 +2086,17 @@ function visit(
2146
2086
  return value ;
2147
2087
  }
2148
2088
 
2149
- // We can set `__sentry_override_normalization_depth__` on an object to ensure that from there
2150
- // We keep a certain amount of depth.
2151
- // This should be used sparingly, e.g. we use it for the redux integration to ensure we get a certain amount of state.
2152
- const remainingDepth =
2153
- typeof (value )['__sentry_override_normalization_depth__'] === 'number'
2154
- ? ((value )['__sentry_override_normalization_depth__'] )
2155
- : depth;
2089
+ // Do not normalize objects that we know have already been normalized. As a general rule, the
2090
+ // "__sentry_skip_normalization__" property should only be used sparingly and only should only be set on objects that
2091
+ // have already been normalized.
2092
+ let overriddenDepth = depth;
2093
+
2094
+ if (typeof (value )['__sentry_override_normalization_depth__'] === 'number') {
2095
+ overriddenDepth = (value )['__sentry_override_normalization_depth__'] ;
2096
+ }
2156
2097
 
2157
2098
  // We're also done if we've reached the max depth
2158
- if (remainingDepth === 0) {
2099
+ if (overriddenDepth === 0) {
2159
2100
  // At this point we know `serialized` is a string of the form `"[object XXXX]"`. Clean it up so it's just `"[XXXX]"`.
2160
2101
  return stringified.replace('object ', '');
2161
2102
  }
@@ -2171,7 +2112,7 @@ function visit(
2171
2112
  try {
2172
2113
  const jsonValue = valueWithToJSON.toJSON();
2173
2114
  // We need to normalize the return value of `.toJSON()` in case it has circular references
2174
- return visit('', jsonValue, remainingDepth - 1, maxProperties, memo);
2115
+ return visit('', jsonValue, overriddenDepth - 1, maxProperties, memo);
2175
2116
  } catch (err) {
2176
2117
  // pass (The built-in `toJSON` failed, but we can still try to do it ourselves)
2177
2118
  }
@@ -2200,7 +2141,7 @@ function visit(
2200
2141
 
2201
2142
  // Recursively visit all the child nodes
2202
2143
  const visitValue = visitable[visitKey];
2203
- normalized[visitKey] = visit(visitKey, visitValue, remainingDepth - 1, maxProperties, memo);
2144
+ normalized[visitKey] = visit(visitKey, visitValue, overriddenDepth - 1, maxProperties, memo);
2204
2145
 
2205
2146
  numAdded++;
2206
2147
  }
@@ -2212,7 +2153,6 @@ function visit(
2212
2153
  return normalized;
2213
2154
  }
2214
2155
 
2215
- /* eslint-disable complexity */
2216
2156
  /**
2217
2157
  * Stringify the given value. Handles various known special values and types.
2218
2158
  *
@@ -2263,6 +2203,11 @@ function stringifyValue(
2263
2203
  return '[NaN]';
2264
2204
  }
2265
2205
 
2206
+ // this catches `undefined` (but not `null`, which is a primitive and can be serialized on its own)
2207
+ if (value === void 0) {
2208
+ return '[undefined]';
2209
+ }
2210
+
2266
2211
  if (typeof value === 'function') {
2267
2212
  return `[Function: ${getFunctionName(value)}]`;
2268
2213
  }
@@ -2280,19 +2225,11 @@ function stringifyValue(
2280
2225
  // them to strings means that instances of classes which haven't defined their `toStringTag` will just come out as
2281
2226
  // `"[object Object]"`. If we instead look at the constructor's name (which is the same as the name of the class),
2282
2227
  // we can make sure that only plain objects come out that way.
2283
- const objName = getConstructorName(value);
2284
-
2285
- // Handle HTML Elements
2286
- if (/^HTML(\w*)Element$/.test(objName)) {
2287
- return `[HTMLElement: ${objName}]`;
2288
- }
2289
-
2290
- return `[object ${objName}]`;
2228
+ return `[object ${getConstructorName(value)}]`;
2291
2229
  } catch (err) {
2292
2230
  return `**non-serializable** (${err})`;
2293
2231
  }
2294
2232
  }
2295
- /* eslint-enable complexity */
2296
2233
 
2297
2234
  function getConstructorName(value) {
2298
2235
  const prototype = Object.getPrototypeOf(value);
@@ -2603,7 +2540,9 @@ function makePromiseBuffer(limit) {
2603
2540
  * // environments where DOM might not be available
2604
2541
  * @returns parsed URL object
2605
2542
  */
2606
- function parseUrl(url) {
2543
+ function parseUrl(url)
2544
+
2545
+ {
2607
2546
  if (!url) {
2608
2547
  return {};
2609
2548
  }
@@ -2621,8 +2560,6 @@ function parseUrl(url) {
2621
2560
  host: match[4],
2622
2561
  path: match[5],
2623
2562
  protocol: match[2],
2624
- search: query,
2625
- hash: fragment,
2626
2563
  relative: match[5] + query + fragment, // everything minus origin
2627
2564
  };
2628
2565
  }
@@ -3007,7 +2944,6 @@ const ITEM_TYPE_TO_DATA_CATEGORY_MAP = {
3007
2944
  profile: 'profile',
3008
2945
  replay_event: 'replay',
3009
2946
  replay_recording: 'replay',
3010
- check_in: 'monitor',
3011
2947
  };
3012
2948
 
3013
2949
  /**
@@ -3037,14 +2973,16 @@ function createEventEnvelopeHeaders(
3037
2973
  dsn,
3038
2974
  ) {
3039
2975
  const dynamicSamplingContext = event.sdkProcessingMetadata && event.sdkProcessingMetadata.dynamicSamplingContext;
2976
+
3040
2977
  return {
3041
2978
  event_id: event.event_id ,
3042
2979
  sent_at: new Date().toISOString(),
3043
2980
  ...(sdkInfo && { sdk: sdkInfo }),
3044
2981
  ...(!!tunnel && { dsn: dsnToString(dsn) }),
3045
- ...(dynamicSamplingContext && {
3046
- trace: dropUndefinedKeys({ ...dynamicSamplingContext }),
3047
- }),
2982
+ ...(event.type === 'transaction' &&
2983
+ dynamicSamplingContext && {
2984
+ trace: dropUndefinedKeys({ ...dynamicSamplingContext }),
2985
+ }),
3048
2986
  };
3049
2987
  }
3050
2988
 
@@ -3749,16 +3687,9 @@ class Scope {
3749
3687
  // errors with transaction and it relies on that.
3750
3688
  if (this._span) {
3751
3689
  event.contexts = { trace: this._span.getTraceContext(), ...event.contexts };
3752
- const transaction = this._span.transaction;
3753
- if (transaction) {
3754
- event.sdkProcessingMetadata = {
3755
- dynamicSamplingContext: transaction.getDynamicSamplingContext(),
3756
- ...event.sdkProcessingMetadata,
3757
- };
3758
- const transactionName = transaction.name;
3759
- if (transactionName) {
3760
- event.tags = { transaction: transactionName, ...event.tags };
3761
- }
3690
+ const transactionName = this._span.transaction && this._span.transaction.name;
3691
+ if (transactionName) {
3692
+ event.tags = { transaction: transactionName, ...event.tags };
3762
3693
  }
3763
3694
  }
3764
3695
 
@@ -3882,6 +3813,11 @@ const API_VERSION = 4;
3882
3813
  */
3883
3814
  const DEFAULT_BREADCRUMBS = 100;
3884
3815
 
3816
+ /**
3817
+ * A layer in the process stack.
3818
+ * @hidden
3819
+ */
3820
+
3885
3821
  /**
3886
3822
  * @inheritDoc
3887
3823
  */
@@ -4159,17 +4095,7 @@ class Hub {
4159
4095
  * @inheritDoc
4160
4096
  */
4161
4097
  startTransaction(context, customSamplingContext) {
4162
- const result = this._callExtensionMethod('startTransaction', context, customSamplingContext);
4163
-
4164
- if ((typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && !result) {
4165
- // eslint-disable-next-line no-console
4166
- console.warn(`Tracing extension 'startTransaction' has not been added. Call 'addTracingExtensions' before calling 'init':
4167
- Sentry.addTracingExtensions();
4168
- Sentry.init({...});
4169
- `);
4170
- }
4171
-
4172
- return result;
4098
+ return this._callExtensionMethod('startTransaction', context, customSamplingContext);
4173
4099
  }
4174
4100
 
4175
4101
  /**
@@ -4254,10 +4180,13 @@ Sentry.init({...});
4254
4180
  */
4255
4181
  _sendSessionUpdate() {
4256
4182
  const { scope, client } = this.getStackTop();
4183
+ if (!scope) return;
4257
4184
 
4258
4185
  const session = scope.getSession();
4259
- if (session && client && client.captureSession) {
4260
- client.captureSession(session);
4186
+ if (session) {
4187
+ if (client && client.captureSession) {
4188
+ client.captureSession(session);
4189
+ }
4261
4190
  }
4262
4191
  }
4263
4192
 
@@ -4327,28 +4256,47 @@ function getCurrentHub() {
4327
4256
  // Get main carrier (global for every environment)
4328
4257
  const registry = getMainCarrier();
4329
4258
 
4330
- if (registry.__SENTRY__ && registry.__SENTRY__.acs) {
4331
- const hub = registry.__SENTRY__.acs.getCurrentHub();
4332
-
4333
- if (hub) {
4334
- return hub;
4335
- }
4336
- }
4337
-
4338
- // Return hub that lives on a global object
4339
- return getGlobalHub(registry);
4340
- }
4341
-
4342
- function getGlobalHub(registry = getMainCarrier()) {
4343
4259
  // If there's no hub, or its an old API, assign a new one
4344
4260
  if (!hasHubOnCarrier(registry) || getHubFromCarrier(registry).isOlderThan(API_VERSION)) {
4345
4261
  setHubOnCarrier(registry, new Hub());
4346
4262
  }
4347
4263
 
4264
+ // Prefer domains over global if they are there (applicable only to Node environment)
4265
+ if (isNodeEnv()) {
4266
+ return getHubFromActiveDomain(registry);
4267
+ }
4348
4268
  // Return hub that lives on a global object
4349
4269
  return getHubFromCarrier(registry);
4350
4270
  }
4351
4271
 
4272
+ /**
4273
+ * Try to read the hub from an active domain, and fallback to the registry if one doesn't exist
4274
+ * @returns discovered hub
4275
+ */
4276
+ function getHubFromActiveDomain(registry) {
4277
+ try {
4278
+ const sentry = getMainCarrier().__SENTRY__;
4279
+ const activeDomain = sentry && sentry.extensions && sentry.extensions.domain && sentry.extensions.domain.active;
4280
+
4281
+ // If there's no active domain, just return global hub
4282
+ if (!activeDomain) {
4283
+ return getHubFromCarrier(registry);
4284
+ }
4285
+
4286
+ // If there's no hub on current domain, or it's an old API, assign a new one
4287
+ if (!hasHubOnCarrier(activeDomain) || getHubFromCarrier(activeDomain).isOlderThan(API_VERSION)) {
4288
+ const registryHubTopStack = getHubFromCarrier(registry).getStackTop();
4289
+ setHubOnCarrier(activeDomain, new Hub(registryHubTopStack.client, Scope.clone(registryHubTopStack.scope)));
4290
+ }
4291
+
4292
+ // Return hub that lives on a domain
4293
+ return getHubFromCarrier(activeDomain);
4294
+ } catch (_Oo) {
4295
+ // Return hub that lives on a global object
4296
+ return getHubFromCarrier(registry);
4297
+ }
4298
+ }
4299
+
4352
4300
  /**
4353
4301
  * This will tell whether a carrier has a hub on it or not
4354
4302
  * @param carrier object
@@ -4422,69 +4370,6 @@ var SpanStatus; (function (SpanStatus) {
4422
4370
  const DataLoss = 'data_loss'; SpanStatus["DataLoss"] = DataLoss;
4423
4371
  })(SpanStatus || (SpanStatus = {}));
4424
4372
 
4425
- /**
4426
- * Wraps a function with a transaction/span and finishes the span after the function is done.
4427
- *
4428
- * Note that if you have not enabled tracing extensions via `addTracingExtensions`, this function
4429
- * will not generate spans, and the `span` returned from the callback may be undefined.
4430
- *
4431
- * This function is meant to be used internally and may break at any time. Use at your own risk.
4432
- *
4433
- * @internal
4434
- * @private
4435
- */
4436
- function trace(
4437
- context,
4438
- callback,
4439
- // eslint-disable-next-line @typescript-eslint/no-empty-function
4440
- onError = () => {},
4441
- ) {
4442
- const ctx = { ...context };
4443
- // If a name is set and a description is not, set the description to the name.
4444
- if (ctx.name !== undefined && ctx.description === undefined) {
4445
- ctx.description = ctx.name;
4446
- }
4447
-
4448
- const hub = getCurrentHub();
4449
- const scope = hub.getScope();
4450
-
4451
- const parentSpan = scope.getSpan();
4452
- const activeSpan = parentSpan ? parentSpan.startChild(ctx) : hub.startTransaction(ctx);
4453
- scope.setSpan(activeSpan);
4454
-
4455
- function finishAndSetSpan() {
4456
- activeSpan && activeSpan.finish();
4457
- hub.getScope().setSpan(parentSpan);
4458
- }
4459
-
4460
- let maybePromiseResult;
4461
- try {
4462
- maybePromiseResult = callback(activeSpan);
4463
- } catch (e) {
4464
- activeSpan && activeSpan.setStatus('internal_error');
4465
- onError(e);
4466
- finishAndSetSpan();
4467
- throw e;
4468
- }
4469
-
4470
- if (isThenable(maybePromiseResult)) {
4471
- Promise.resolve(maybePromiseResult).then(
4472
- () => {
4473
- finishAndSetSpan();
4474
- },
4475
- e => {
4476
- activeSpan && activeSpan.setStatus('internal_error');
4477
- onError(e);
4478
- finishAndSetSpan();
4479
- },
4480
- );
4481
- } else {
4482
- finishAndSetSpan();
4483
- }
4484
-
4485
- return maybePromiseResult;
4486
- }
4487
-
4488
4373
  // Note: All functions in this file are typed with a return value of `ReturnType<Hub[HUB_FUNCTION]>`,
4489
4374
  // where HUB_FUNCTION is some method on the Hub class.
4490
4375
  //
@@ -4826,11 +4711,7 @@ function prepareEvent(
4826
4711
 
4827
4712
  applyClientOptions(prepared, options);
4828
4713
  applyIntegrationsMetadata(prepared, integrations);
4829
-
4830
- // Only apply debug metadata to error events.
4831
- if (event.type === undefined) {
4832
- applyDebugMetadata(prepared, options.stackParser);
4833
- }
4714
+ applyDebugMetadata(prepared, options.stackParser);
4834
4715
 
4835
4716
  // If we have scope given to us, use it as the base for further modifications.
4836
4717
  // This allows us to prevent unnecessary copying of data if `captureContext` is not provided.
@@ -4907,8 +4788,6 @@ function applyClientOptions(event, options) {
4907
4788
  }
4908
4789
  }
4909
4790
 
4910
- const debugIdStackParserCache = new WeakMap();
4911
-
4912
4791
  /**
4913
4792
  * Applies debug metadata images to the event in order to apply source maps by looking up their debug ID.
4914
4793
  */
@@ -4919,28 +4798,10 @@ function applyDebugMetadata(event, stackParser) {
4919
4798
  return;
4920
4799
  }
4921
4800
 
4922
- let debugIdStackFramesCache;
4923
- const cachedDebugIdStackFrameCache = debugIdStackParserCache.get(stackParser);
4924
- if (cachedDebugIdStackFrameCache) {
4925
- debugIdStackFramesCache = cachedDebugIdStackFrameCache;
4926
- } else {
4927
- debugIdStackFramesCache = new Map();
4928
- debugIdStackParserCache.set(stackParser, debugIdStackFramesCache);
4929
- }
4930
-
4931
4801
  // Build a map of filename -> debug_id
4932
4802
  const filenameDebugIdMap = Object.keys(debugIdMap).reduce((acc, debugIdStackTrace) => {
4933
- let parsedStack;
4934
- const cachedParsedStack = debugIdStackFramesCache.get(debugIdStackTrace);
4935
- if (cachedParsedStack) {
4936
- parsedStack = cachedParsedStack;
4937
- } else {
4938
- parsedStack = stackParser(debugIdStackTrace);
4939
- debugIdStackFramesCache.set(debugIdStackTrace, parsedStack);
4940
- }
4941
-
4942
- for (let i = parsedStack.length - 1; i >= 0; i--) {
4943
- const stackFrame = parsedStack[i];
4803
+ const parsedStack = stackParser(debugIdStackTrace);
4804
+ for (const stackFrame of parsedStack) {
4944
4805
  if (stackFrame.filename) {
4945
4806
  acc[stackFrame.filename] = debugIdMap[debugIdStackTrace];
4946
4807
  break;
@@ -5845,7 +5706,7 @@ function getEventForEnvelopeItem(item, type) {
5845
5706
  return Array.isArray(item) ? (item )[1] : undefined;
5846
5707
  }
5847
5708
 
5848
- const SDK_VERSION = '7.52.1';
5709
+ const SDK_VERSION = '7.46.0';
5849
5710
 
5850
5711
  let originalFunctionToString;
5851
5712
 
@@ -5868,17 +5729,11 @@ class FunctionToString {constructor() { FunctionToString.prototype.__init.call(
5868
5729
  // eslint-disable-next-line @typescript-eslint/unbound-method
5869
5730
  originalFunctionToString = Function.prototype.toString;
5870
5731
 
5871
- // intrinsics (like Function.prototype) might be immutable in some environments
5872
- // e.g. Node with --frozen-intrinsics, XS (an embedded JavaScript engine) or SES (a JavaScript proposal)
5873
- try {
5874
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
5875
- Function.prototype.toString = function ( ...args) {
5876
- const context = getOriginalFunction(this) || this;
5877
- return originalFunctionToString.apply(context, args);
5878
- };
5879
- } catch (e) {
5880
- // ignore errors here, just don't patch this
5881
- }
5732
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
5733
+ Function.prototype.toString = function ( ...args) {
5734
+ const context = getOriginalFunction(this) || this;
5735
+ return originalFunctionToString.apply(context, args);
5736
+ };
5882
5737
  }
5883
5738
  } FunctionToString.__initStatic();
5884
5739
 
@@ -6026,9 +5881,8 @@ function _getPossibleEventMessages(event) {
6026
5881
  return [event.message];
6027
5882
  }
6028
5883
  if (event.exception) {
6029
- const { values } = event.exception;
6030
5884
  try {
6031
- const { type = '', value = '' } = (values && values[values.length - 1]) || {};
5885
+ const { type = '', value = '' } = (event.exception.values && event.exception.values[0]) || {};
6032
5886
  return [`${value}`, `${type}: ${value}`];
6033
5887
  } catch (oO) {
6034
5888
  (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.error(`Cannot extract message for event ${getEventDescription(event)}`);
@@ -6687,14 +6541,12 @@ function _consoleBreadcrumb(handlerData) {
6687
6541
  function _xhrBreadcrumb(handlerData) {
6688
6542
  const { startTimestamp, endTimestamp } = handlerData;
6689
6543
 
6690
- const sentryXhrData = handlerData.xhr[SENTRY_XHR_DATA_KEY];
6691
-
6692
6544
  // We only capture complete, non-sentry requests
6693
- if (!startTimestamp || !endTimestamp || !sentryXhrData) {
6545
+ if (!startTimestamp || !endTimestamp || !handlerData.xhr.__sentry_xhr__) {
6694
6546
  return;
6695
6547
  }
6696
6548
 
6697
- const { method, url, status_code, body } = sentryXhrData;
6549
+ const { method, url, status_code, body } = handlerData.xhr.__sentry_xhr__;
6698
6550
 
6699
6551
  const data = {
6700
6552
  method,
@@ -6812,43 +6664,6 @@ function _isEvent(event) {
6812
6664
  return event && !!(event ).target;
6813
6665
  }
6814
6666
 
6815
- /**
6816
- * Creates an envelope from a user feedback.
6817
- */
6818
- function createUserFeedbackEnvelope(
6819
- feedback,
6820
- {
6821
- metadata,
6822
- tunnel,
6823
- dsn,
6824
- }
6825
-
6826
- ,
6827
- ) {
6828
- const headers = {
6829
- event_id: feedback.event_id,
6830
- sent_at: new Date().toISOString(),
6831
- ...(metadata &&
6832
- metadata.sdk && {
6833
- sdk: {
6834
- name: metadata.sdk.name,
6835
- version: metadata.sdk.version,
6836
- },
6837
- }),
6838
- ...(!!tunnel && !!dsn && { dsn: dsnToString(dsn) }),
6839
- };
6840
- const item = createUserFeedbackEnvelopeItem(feedback);
6841
-
6842
- return createEnvelope(headers, [item]);
6843
- }
6844
-
6845
- function createUserFeedbackEnvelopeItem(feedback) {
6846
- const feedbackHeaders = {
6847
- type: 'user_report',
6848
- };
6849
- return [feedbackHeaders, feedback];
6850
- }
6851
-
6852
6667
  /**
6853
6668
  * Configuration options for the Sentry Browser SDK.
6854
6669
  * @see @sentry/types Options for more information.
@@ -6931,23 +6746,6 @@ class BrowserClient extends BaseClient {
6931
6746
  super.sendEvent(event, hint);
6932
6747
  }
6933
6748
 
6934
- /**
6935
- * Sends user feedback to Sentry.
6936
- */
6937
- captureUserFeedback(feedback) {
6938
- if (!this._isEnabled()) {
6939
- (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.warn('SDK not enabled, will not capture user feedback.');
6940
- return;
6941
- }
6942
-
6943
- const envelope = createUserFeedbackEnvelope(feedback, {
6944
- metadata: this.getSdkMetadata(),
6945
- dsn: this.getDsn(),
6946
- tunnel: this.getOptions().tunnel,
6947
- });
6948
- void this._sendEnvelope(envelope);
6949
- }
6950
-
6951
6749
  /**
6952
6750
  * @inheritDoc
6953
6751
  */
@@ -7190,7 +6988,7 @@ function createFrame(filename, func, lineno, colno) {
7190
6988
 
7191
6989
  // Chromium based browsers: Chrome, Brave, new Opera, new Edge
7192
6990
  const chromeRegex =
7193
- /^\s*at (?:(.+?\)(?: \[.+\])?|.*?) ?\((?:address at )?)?(?:async )?((?:<anonymous>|[-a-z]+:|.*bundle|\/)?.*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i;
6991
+ /^\s*at (?:(.*\).*?|.*?) ?\((?:address at )?)?(?:async )?((?:file|https?|blob|chrome-extension|address|native|eval|webpack|<anonymous>|[-a-z]+:|.*bundle|\/)?.*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i;
7194
6992
  const chromeEvalRegex = /\((\S*)(?::(\d+))(?::(\d+))\)/;
7195
6993
 
7196
6994
  const chrome = line => {
@@ -7226,7 +7024,7 @@ const chromeStackLineParser = [CHROME_PRIORITY, chrome];
7226
7024
  // generates filenames without a prefix like `file://` the filenames in the stacktrace are just 42.js
7227
7025
  // We need this specific case for now because we want no other regex to match.
7228
7026
  const geckoREgex =
7229
- /^\s*(.*?)(?:\((.*?)\))?(?:^|@)?((?:[-a-z]+)?:\/.*?|\[native code\]|[^@]*(?:bundle|\d+\.js)|\/[\w\-. /=]+)(?::(\d+))?(?::(\d+))?\s*$/i;
7027
+ /^\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;
7230
7028
  const geckoEvalRegex = /(\S+) line (\d+)(?: > eval line \d+)* > eval/i;
7231
7029
 
7232
7030
  const gecko = line => {
@@ -7258,7 +7056,8 @@ const gecko = line => {
7258
7056
 
7259
7057
  const geckoStackLineParser = [GECKO_PRIORITY, gecko];
7260
7058
 
7261
- const winjsRegex = /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:[-a-z]+):.*?):(\d+)(?::(\d+))?\)?\s*$/i;
7059
+ const winjsRegex =
7060
+ /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:file|ms-appx|https?|webpack|blob):.*?):(\d+)(?::(\d+))?\)?\s*$/i;
7262
7061
 
7263
7062
  const winjs = line => {
7264
7063
  const parts = winjsRegex.exec(line);
@@ -8323,14 +8122,11 @@ const REPLAY_SESSION_KEY = 'sentryReplaySession';
8323
8122
  const REPLAY_EVENT_NAME = 'replay_event';
8324
8123
  const UNABLE_TO_SEND_REPLAY = 'Unable to send Replay';
8325
8124
 
8326
- // The idle limit for a session after which recording is paused.
8327
- const SESSION_IDLE_PAUSE_DURATION = 300000; // 5 minutes in ms
8328
-
8329
- // The idle limit for a session after which the session expires.
8330
- const SESSION_IDLE_EXPIRE_DURATION = 900000; // 15 minutes in ms
8125
+ // The idle limit for a session
8126
+ const SESSION_IDLE_DURATION = 300000; // 5 minutes in ms
8331
8127
 
8332
8128
  // The maximum length of a session
8333
- const MAX_SESSION_LIFE = 3600000; // 60 minutes in ms
8129
+ const MAX_SESSION_LIFE = 3600000; // 60 minutes
8334
8130
 
8335
8131
  /** Default flush delays */
8336
8132
  const DEFAULT_FLUSH_MIN_DELAY = 5000;
@@ -8339,16 +8135,13 @@ const DEFAULT_FLUSH_MIN_DELAY = 5000;
8339
8135
  const DEFAULT_FLUSH_MAX_DELAY = 5500;
8340
8136
 
8341
8137
  /* How long to wait for error checkouts */
8342
- const BUFFER_CHECKOUT_TIME = 60000;
8138
+ const ERROR_CHECKOUT_TIME = 60000;
8343
8139
 
8344
8140
  const RETRY_BASE_INTERVAL = 5000;
8345
8141
  const RETRY_MAX_COUNT = 3;
8346
8142
 
8347
- /* The max (uncompressed) size in bytes of a network body. Any body larger than this will be truncated. */
8348
- const NETWORK_BODY_MAX_SIZE = 150000;
8349
-
8350
- /* The max size of a single console arg that is captured. Any arg larger than this will be truncated. */
8351
- const CONSOLE_ARG_MAX_SIZE = 5000;
8143
+ /* The max (uncompressed) size in bytes of a network body. Any body larger than this will be dropped. */
8144
+ const NETWORK_BODY_MAX_SIZE = 300000;
8352
8145
 
8353
8146
  var NodeType$1;
8354
8147
  (function (NodeType) {
@@ -8385,7 +8178,7 @@ function maskInputValue({ input, maskInputSelector, unmaskInputSelector, maskInp
8385
8178
  if (unmaskInputSelector && input.matches(unmaskInputSelector)) {
8386
8179
  return text;
8387
8180
  }
8388
- if (input.hasAttribute('data-rr-is-password')) {
8181
+ if (input.hasAttribute('rr_is_password')) {
8389
8182
  type = 'password';
8390
8183
  }
8391
8184
  if (isInputTypeMasked({ maskInputOptions, tagName, type }) ||
@@ -8418,21 +8211,6 @@ function is2DCanvasBlank(canvas) {
8418
8211
  }
8419
8212
  return true;
8420
8213
  }
8421
- function getInputType(element) {
8422
- const type = element.type;
8423
- return element.hasAttribute('data-rr-is-password')
8424
- ? 'password'
8425
- : type
8426
- ? type.toLowerCase()
8427
- : null;
8428
- }
8429
- function getInputValue(el, tagName, type) {
8430
- typeof type === 'string' ? type.toLowerCase() : '';
8431
- if (tagName === 'INPUT' && (type === 'radio' || type === 'checkbox')) {
8432
- return el.getAttribute('value') || '';
8433
- }
8434
- return el.value;
8435
- }
8436
8214
 
8437
8215
  let _id = 1;
8438
8216
  const tagNameRegex = new RegExp('[^a-z0-9-_:]');
@@ -8471,13 +8249,6 @@ function getCssRuleString(rule) {
8471
8249
  catch (_a) {
8472
8250
  }
8473
8251
  }
8474
- return validateStringifiedCssRule(cssStringified);
8475
- }
8476
- function validateStringifiedCssRule(cssStringified) {
8477
- if (cssStringified.indexOf(':') > -1) {
8478
- const regex = /(\[(?:[\w-]+)[^\\])(:(?:[\w-]+)\])/gm;
8479
- return cssStringified.replace(regex, '$1\\$2');
8480
- }
8481
8252
  return cssStringified;
8482
8253
  }
8483
8254
  function isCSSImportRule(rule) {
@@ -8486,7 +8257,7 @@ function isCSSImportRule(rule) {
8486
8257
  function stringifyStyleSheet(sheet) {
8487
8258
  return sheet.cssRules
8488
8259
  ? Array.from(sheet.cssRules)
8489
- .map((rule) => rule.cssText ? validateStringifiedCssRule(rule.cssText) : '')
8260
+ .map((rule) => rule.cssText || '')
8490
8261
  .join('')
8491
8262
  : '';
8492
8263
  }
@@ -8821,15 +8592,14 @@ function serializeNode(n, options) {
8821
8592
  tagName === 'select' ||
8822
8593
  tagName === 'option') {
8823
8594
  const el = n;
8824
- const type = getInputType(el);
8825
- const value = getInputValue(el, tagName.toUpperCase(), type);
8595
+ const value = getInputValue(tagName, el, attributes);
8826
8596
  const checked = n.checked;
8827
- if (type !== 'submit' &&
8828
- type !== 'button' &&
8597
+ if (attributes.type !== 'submit' &&
8598
+ attributes.type !== 'button' &&
8829
8599
  value) {
8830
8600
  attributes.value = maskInputValue({
8831
8601
  input: el,
8832
- type,
8602
+ type: attributes.type,
8833
8603
  tagName,
8834
8604
  value,
8835
8605
  maskInputSelector,
@@ -9302,8 +9072,15 @@ function snapshot(n, options) {
9302
9072
  function skipAttribute(tagName, attributeName, value) {
9303
9073
  return ((tagName === 'video' || tagName === 'audio') && attributeName === 'autoplay');
9304
9074
  }
9075
+ function getInputValue(tagName, el, attributes) {
9076
+ if (tagName === 'input' &&
9077
+ (attributes.type === 'radio' || attributes.type === 'checkbox')) {
9078
+ return el.getAttribute('value') || '';
9079
+ }
9080
+ return el.value;
9081
+ }
9305
9082
 
9306
- var EventType$1;
9083
+ var EventType;
9307
9084
  (function (EventType) {
9308
9085
  EventType[EventType["DomContentLoaded"] = 0] = "DomContentLoaded";
9309
9086
  EventType[EventType["Load"] = 1] = "Load";
@@ -9312,7 +9089,7 @@ var EventType$1;
9312
9089
  EventType[EventType["Meta"] = 4] = "Meta";
9313
9090
  EventType[EventType["Custom"] = 5] = "Custom";
9314
9091
  EventType[EventType["Plugin"] = 6] = "Plugin";
9315
- })(EventType$1 || (EventType$1 = {}));
9092
+ })(EventType || (EventType = {}));
9316
9093
  var IncrementalSource;
9317
9094
  (function (IncrementalSource) {
9318
9095
  IncrementalSource[IncrementalSource["Mutation"] = 0] = "Mutation";
@@ -9927,9 +9704,9 @@ class MutationBuffer {
9927
9704
  this.attributes.push(item);
9928
9705
  }
9929
9706
  if (m.attributeName === 'type' &&
9930
- target.tagName === 'INPUT' &&
9707
+ m.target.tagName === 'INPUT' &&
9931
9708
  (m.oldValue || '').toLowerCase() === 'password') {
9932
- target.setAttribute('data-rr-is-password', 'true');
9709
+ m.target.setAttribute('rr_is_password', 'true');
9933
9710
  }
9934
9711
  if (m.attributeName === 'style') {
9935
9712
  const old = this.doc.createElement('span');
@@ -10326,25 +10103,27 @@ function initInputObserver({ inputCb, doc, mirror, blockClass, blockSelector, un
10326
10103
  isBlocked(target, blockClass, blockSelector, unblockSelector)) {
10327
10104
  return;
10328
10105
  }
10329
- const el = target;
10330
- const type = getInputType(el);
10331
- if (el.classList.contains(ignoreClass) ||
10332
- (ignoreSelector && el.matches(ignoreSelector))) {
10106
+ let type = target.type;
10107
+ if (target.classList.contains(ignoreClass) ||
10108
+ (ignoreSelector && target.matches(ignoreSelector))) {
10333
10109
  return;
10334
10110
  }
10335
- let text = getInputValue(el, tagName, type);
10111
+ let text = target.value;
10336
10112
  let isChecked = false;
10113
+ if (target.hasAttribute('rr_is_password')) {
10114
+ type = 'password';
10115
+ }
10337
10116
  if (type === 'radio' || type === 'checkbox') {
10338
10117
  isChecked = target.checked;
10339
10118
  }
10340
- if (hasInputMaskOptions({
10119
+ else if (hasInputMaskOptions({
10341
10120
  maskInputOptions,
10342
10121
  maskInputSelector,
10343
10122
  tagName,
10344
10123
  type,
10345
10124
  })) {
10346
10125
  text = maskInputValue({
10347
- input: el,
10126
+ input: target,
10348
10127
  maskInputOptions,
10349
10128
  maskInputSelector,
10350
10129
  unmaskInputSelector,
@@ -10361,18 +10140,8 @@ function initInputObserver({ inputCb, doc, mirror, blockClass, blockSelector, un
10361
10140
  .querySelectorAll(`input[type="radio"][name="${name}"]`)
10362
10141
  .forEach((el) => {
10363
10142
  if (el !== target) {
10364
- const text = maskInputValue({
10365
- input: el,
10366
- maskInputOptions,
10367
- maskInputSelector,
10368
- unmaskInputSelector,
10369
- tagName,
10370
- type,
10371
- value: getInputValue(el, tagName, type),
10372
- maskInputFn,
10373
- });
10374
10143
  cbWithDedup(el, callbackWrapper(wrapEventWithUserTriggeredFlag)({
10375
- text,
10144
+ text: el.value,
10376
10145
  isChecked: !isChecked,
10377
10146
  userTriggered: false,
10378
10147
  }, userTriggeredOnInput));
@@ -11285,17 +11054,17 @@ function record(options = {}) {
11285
11054
  wrappedEmit = (e, isCheckout) => {
11286
11055
  var _a;
11287
11056
  if (((_a = mutationBuffers[0]) === null || _a === void 0 ? void 0 : _a.isFrozen()) &&
11288
- e.type !== EventType$1.FullSnapshot &&
11289
- !(e.type === EventType$1.IncrementalSnapshot &&
11057
+ e.type !== EventType.FullSnapshot &&
11058
+ !(e.type === EventType.IncrementalSnapshot &&
11290
11059
  e.data.source === IncrementalSource.Mutation)) {
11291
11060
  mutationBuffers.forEach((buf) => buf.unfreeze());
11292
11061
  }
11293
11062
  emit(eventProcessor(e), isCheckout);
11294
- if (e.type === EventType$1.FullSnapshot) {
11063
+ if (e.type === EventType.FullSnapshot) {
11295
11064
  lastFullSnapshotEvent = e;
11296
11065
  incrementalSnapshotCount = 0;
11297
11066
  }
11298
- else if (e.type === EventType$1.IncrementalSnapshot) {
11067
+ else if (e.type === EventType.IncrementalSnapshot) {
11299
11068
  if (e.data.source === IncrementalSource.Mutation &&
11300
11069
  e.data.isAttachIframe) {
11301
11070
  return;
@@ -11311,16 +11080,16 @@ function record(options = {}) {
11311
11080
  };
11312
11081
  const wrappedMutationEmit = (m) => {
11313
11082
  wrappedEmit(wrapEvent({
11314
- type: EventType$1.IncrementalSnapshot,
11083
+ type: EventType.IncrementalSnapshot,
11315
11084
  data: Object.assign({ source: IncrementalSource.Mutation }, m),
11316
11085
  }));
11317
11086
  };
11318
11087
  const wrappedScrollEmit = (p) => wrappedEmit(wrapEvent({
11319
- type: EventType$1.IncrementalSnapshot,
11088
+ type: EventType.IncrementalSnapshot,
11320
11089
  data: Object.assign({ source: IncrementalSource.Scroll }, p),
11321
11090
  }));
11322
11091
  const wrappedCanvasMutationEmit = (p) => wrappedEmit(wrapEvent({
11323
- type: EventType$1.IncrementalSnapshot,
11092
+ type: EventType.IncrementalSnapshot,
11324
11093
  data: Object.assign({ source: IncrementalSource.CanvasMutation }, p),
11325
11094
  }));
11326
11095
  const iframeManager = new IframeManager({
@@ -11365,7 +11134,7 @@ function record(options = {}) {
11365
11134
  takeFullSnapshot = (isCheckout = false) => {
11366
11135
  var _a, _b, _c, _d;
11367
11136
  wrappedEmit(wrapEvent({
11368
- type: EventType$1.Meta,
11137
+ type: EventType.Meta,
11369
11138
  data: {
11370
11139
  href: window.location.href,
11371
11140
  width: getWindowWidth(),
@@ -11408,7 +11177,7 @@ function record(options = {}) {
11408
11177
  }
11409
11178
  mirror.map = idNodeMap;
11410
11179
  wrappedEmit(wrapEvent({
11411
- type: EventType$1.FullSnapshot,
11180
+ type: EventType.FullSnapshot,
11412
11181
  data: {
11413
11182
  node,
11414
11183
  initialOffset: {
@@ -11433,7 +11202,7 @@ function record(options = {}) {
11433
11202
  const handlers = [];
11434
11203
  handlers.push(on$1('DOMContentLoaded', () => {
11435
11204
  wrappedEmit(wrapEvent({
11436
- type: EventType$1.DomContentLoaded,
11205
+ type: EventType.DomContentLoaded,
11437
11206
  data: {},
11438
11207
  }));
11439
11208
  }));
@@ -11443,40 +11212,40 @@ function record(options = {}) {
11443
11212
  onMutation,
11444
11213
  mutationCb: wrappedMutationEmit,
11445
11214
  mousemoveCb: (positions, source) => wrappedEmit(wrapEvent({
11446
- type: EventType$1.IncrementalSnapshot,
11215
+ type: EventType.IncrementalSnapshot,
11447
11216
  data: {
11448
11217
  source,
11449
11218
  positions,
11450
11219
  },
11451
11220
  })),
11452
11221
  mouseInteractionCb: (d) => wrappedEmit(wrapEvent({
11453
- type: EventType$1.IncrementalSnapshot,
11222
+ type: EventType.IncrementalSnapshot,
11454
11223
  data: Object.assign({ source: IncrementalSource.MouseInteraction }, d),
11455
11224
  })),
11456
11225
  scrollCb: wrappedScrollEmit,
11457
11226
  viewportResizeCb: (d) => wrappedEmit(wrapEvent({
11458
- type: EventType$1.IncrementalSnapshot,
11227
+ type: EventType.IncrementalSnapshot,
11459
11228
  data: Object.assign({ source: IncrementalSource.ViewportResize }, d),
11460
11229
  })),
11461
11230
  inputCb: (v) => wrappedEmit(wrapEvent({
11462
- type: EventType$1.IncrementalSnapshot,
11231
+ type: EventType.IncrementalSnapshot,
11463
11232
  data: Object.assign({ source: IncrementalSource.Input }, v),
11464
11233
  })),
11465
11234
  mediaInteractionCb: (p) => wrappedEmit(wrapEvent({
11466
- type: EventType$1.IncrementalSnapshot,
11235
+ type: EventType.IncrementalSnapshot,
11467
11236
  data: Object.assign({ source: IncrementalSource.MediaInteraction }, p),
11468
11237
  })),
11469
11238
  styleSheetRuleCb: (r) => wrappedEmit(wrapEvent({
11470
- type: EventType$1.IncrementalSnapshot,
11239
+ type: EventType.IncrementalSnapshot,
11471
11240
  data: Object.assign({ source: IncrementalSource.StyleSheetRule }, r),
11472
11241
  })),
11473
11242
  styleDeclarationCb: (r) => wrappedEmit(wrapEvent({
11474
- type: EventType$1.IncrementalSnapshot,
11243
+ type: EventType.IncrementalSnapshot,
11475
11244
  data: Object.assign({ source: IncrementalSource.StyleDeclaration }, r),
11476
11245
  })),
11477
11246
  canvasMutationCb: wrappedCanvasMutationEmit,
11478
11247
  fontCb: (p) => wrappedEmit(wrapEvent({
11479
- type: EventType$1.IncrementalSnapshot,
11248
+ type: EventType.IncrementalSnapshot,
11480
11249
  data: Object.assign({ source: IncrementalSource.Font }, p),
11481
11250
  })),
11482
11251
  blockClass,
@@ -11509,7 +11278,7 @@ function record(options = {}) {
11509
11278
  observer: p.observer,
11510
11279
  options: p.options,
11511
11280
  callback: (payload) => wrappedEmit(wrapEvent({
11512
- type: EventType$1.Plugin,
11281
+ type: EventType.Plugin,
11513
11282
  data: {
11514
11283
  plugin: p.name,
11515
11284
  payload,
@@ -11537,7 +11306,7 @@ function record(options = {}) {
11537
11306
  else {
11538
11307
  handlers.push(on$1('load', () => {
11539
11308
  wrappedEmit(wrapEvent({
11540
- type: EventType$1.Load,
11309
+ type: EventType.Load,
11541
11310
  data: {},
11542
11311
  }));
11543
11312
  init();
@@ -11556,7 +11325,7 @@ record.addCustomEvent = (tag, payload) => {
11556
11325
  throw new Error('please add custom event after start recording');
11557
11326
  }
11558
11327
  wrappedEmit(wrapEvent({
11559
- type: EventType$1.Custom,
11328
+ type: EventType.Custom,
11560
11329
  data: {
11561
11330
  tag,
11562
11331
  payload,
@@ -11574,475 +11343,6 @@ record.takeFullSnapshot = (isCheckout) => {
11574
11343
  };
11575
11344
  record.mirror = mirror;
11576
11345
 
11577
- /**
11578
- * Create a breadcrumb for a replay.
11579
- */
11580
- function createBreadcrumb(
11581
- breadcrumb,
11582
- ) {
11583
- return {
11584
- timestamp: Date.now() / 1000,
11585
- type: 'default',
11586
- ...breadcrumb,
11587
- };
11588
- }
11589
-
11590
- var NodeType;
11591
- (function (NodeType) {
11592
- NodeType[NodeType["Document"] = 0] = "Document";
11593
- NodeType[NodeType["DocumentType"] = 1] = "DocumentType";
11594
- NodeType[NodeType["Element"] = 2] = "Element";
11595
- NodeType[NodeType["Text"] = 3] = "Text";
11596
- NodeType[NodeType["CDATA"] = 4] = "CDATA";
11597
- NodeType[NodeType["Comment"] = 5] = "Comment";
11598
- })(NodeType || (NodeType = {}));
11599
-
11600
- /**
11601
- * Converts a timestamp to ms, if it was in s, or keeps it as ms.
11602
- */
11603
- function timestampToMs(timestamp) {
11604
- const isMs = timestamp > 9999999999;
11605
- return isMs ? timestamp : timestamp * 1000;
11606
- }
11607
-
11608
- /**
11609
- * Add an event to the event buffer.
11610
- * `isCheckout` is true if this is either the very first event, or an event triggered by `checkoutEveryNms`.
11611
- */
11612
- async function addEvent(
11613
- replay,
11614
- event,
11615
- isCheckout,
11616
- ) {
11617
- if (!replay.eventBuffer) {
11618
- // This implies that `_isEnabled` is false
11619
- return null;
11620
- }
11621
-
11622
- if (replay.isPaused()) {
11623
- // Do not add to event buffer when recording is paused
11624
- return null;
11625
- }
11626
-
11627
- const timestampInMs = timestampToMs(event.timestamp);
11628
-
11629
- // Throw out events that happen more than 5 minutes ago. This can happen if
11630
- // page has been left open and idle for a long period of time and user
11631
- // comes back to trigger a new session. The performance entries rely on
11632
- // `performance.timeOrigin`, which is when the page first opened.
11633
- if (timestampInMs + replay.timeouts.sessionIdlePause < Date.now()) {
11634
- return null;
11635
- }
11636
-
11637
- try {
11638
- if (isCheckout) {
11639
- replay.eventBuffer.clear();
11640
- }
11641
-
11642
- return await replay.eventBuffer.addEvent(event);
11643
- } catch (error) {
11644
- (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.error(error);
11645
- await replay.stop('addEvent');
11646
-
11647
- const client = getCurrentHub().getClient();
11648
-
11649
- if (client) {
11650
- client.recordDroppedEvent('internal_sdk_error', 'replay');
11651
- }
11652
- }
11653
- }
11654
-
11655
- /**
11656
- * Add a breadcrumb event to replay.
11657
- */
11658
- function addBreadcrumbEvent(replay, breadcrumb) {
11659
- if (breadcrumb.category === 'sentry.transaction') {
11660
- return;
11661
- }
11662
-
11663
- if (['ui.click', 'ui.input'].includes(breadcrumb.category )) {
11664
- replay.triggerUserActivity();
11665
- } else {
11666
- replay.checkAndHandleExpiredSession();
11667
- }
11668
-
11669
- replay.addUpdate(() => {
11670
- void addEvent(replay, {
11671
- type: EventType$1.Custom,
11672
- // TODO: We were converting from ms to seconds for breadcrumbs, spans,
11673
- // but maybe we should just keep them as milliseconds
11674
- timestamp: (breadcrumb.timestamp || 0) * 1000,
11675
- data: {
11676
- tag: 'breadcrumb',
11677
- // normalize to max. 10 depth and 1_000 properties per object
11678
- payload: normalize(breadcrumb, 10, 1000),
11679
- },
11680
- });
11681
-
11682
- // Do not flush after console log messages
11683
- return breadcrumb.category === 'console';
11684
- });
11685
- }
11686
-
11687
- /**
11688
- * Detect a slow click on a button/a tag,
11689
- * and potentially create a corresponding breadcrumb.
11690
- */
11691
- function detectSlowClick(
11692
- replay,
11693
- config,
11694
- clickBreadcrumb,
11695
- node,
11696
- ) {
11697
- if (ignoreElement(node, config)) {
11698
- return;
11699
- }
11700
-
11701
- /*
11702
- We consider a slow click a click on a button/a, which does not trigger one of:
11703
- - DOM mutation
11704
- - Scroll (within 100ms)
11705
- Within the given threshold time.
11706
- After time timeout time, we stop listening and mark it as a slow click anyhow.
11707
- */
11708
-
11709
- let cleanup = () => {
11710
- // replaced further down
11711
- };
11712
-
11713
- // After timeout time, def. consider this a slow click, and stop watching for mutations
11714
- const timeout = setTimeout(() => {
11715
- handleSlowClick(replay, clickBreadcrumb, config.timeout, 'timeout');
11716
- cleanup();
11717
- }, config.timeout);
11718
-
11719
- const mutationHandler = () => {
11720
- maybeHandleSlowClick(replay, clickBreadcrumb, config.threshold, config.timeout, 'mutation');
11721
- cleanup();
11722
- };
11723
-
11724
- const scrollHandler = () => {
11725
- maybeHandleSlowClick(replay, clickBreadcrumb, config.scrollTimeout, config.timeout, 'scroll');
11726
- cleanup();
11727
- };
11728
-
11729
- const obs = new MutationObserver(mutationHandler);
11730
-
11731
- obs.observe(WINDOW.document.documentElement, {
11732
- attributes: true,
11733
- characterData: true,
11734
- childList: true,
11735
- subtree: true,
11736
- });
11737
-
11738
- WINDOW.addEventListener('scroll', scrollHandler);
11739
-
11740
- // Stop listening to scroll timeouts early
11741
- const scrollTimeout = setTimeout(() => {
11742
- WINDOW.removeEventListener('scroll', scrollHandler);
11743
- }, config.scrollTimeout);
11744
-
11745
- cleanup = () => {
11746
- clearTimeout(timeout);
11747
- clearTimeout(scrollTimeout);
11748
- obs.disconnect();
11749
- WINDOW.removeEventListener('scroll', scrollHandler);
11750
- };
11751
- }
11752
-
11753
- function maybeHandleSlowClick(
11754
- replay,
11755
- clickBreadcrumb,
11756
- threshold,
11757
- timeout,
11758
- endReason,
11759
- ) {
11760
- const now = Date.now();
11761
- const timeAfterClickMs = now - clickBreadcrumb.timestamp * 1000;
11762
-
11763
- if (timeAfterClickMs > threshold) {
11764
- handleSlowClick(replay, clickBreadcrumb, Math.min(timeAfterClickMs, timeout), endReason);
11765
- return true;
11766
- }
11767
-
11768
- return false;
11769
- }
11770
-
11771
- function handleSlowClick(
11772
- replay,
11773
- clickBreadcrumb,
11774
- timeAfterClickMs,
11775
- endReason,
11776
- ) {
11777
- const breadcrumb = {
11778
- message: clickBreadcrumb.message,
11779
- timestamp: clickBreadcrumb.timestamp,
11780
- category: 'ui.slowClickDetected',
11781
- data: {
11782
- ...clickBreadcrumb.data,
11783
- url: WINDOW.location.href,
11784
- // TODO FN: add parametrized route, when possible
11785
- timeAfterClickMs,
11786
- endReason,
11787
- },
11788
- };
11789
-
11790
- addBreadcrumbEvent(replay, breadcrumb);
11791
- }
11792
-
11793
- const SLOW_CLICK_IGNORE_TAGS = ['SELECT', 'OPTION'];
11794
-
11795
- function ignoreElement(node, config) {
11796
- // If <input> tag, we only want to consider input[type='submit'] & input[type='button']
11797
- if (node.tagName === 'INPUT' && !['submit', 'button'].includes(node.getAttribute('type') || '')) {
11798
- return true;
11799
- }
11800
-
11801
- if (SLOW_CLICK_IGNORE_TAGS.includes(node.tagName)) {
11802
- return true;
11803
- }
11804
-
11805
- // If <a> tag, detect special variants that may not lead to an action
11806
- // If target !== _self, we may open the link somewhere else, which would lead to no action
11807
- // Also, when downloading a file, we may not leave the page, but still not trigger an action
11808
- if (
11809
- node.tagName === 'A' &&
11810
- (node.hasAttribute('download') || (node.hasAttribute('target') && node.getAttribute('target') !== '_self'))
11811
- ) {
11812
- return true;
11813
- }
11814
-
11815
- if (config.ignoreSelector && node.matches(config.ignoreSelector)) {
11816
- return true;
11817
- }
11818
-
11819
- return false;
11820
- }
11821
-
11822
- // Note that these are the serialized attributes and not attributes directly on
11823
- // the DOM Node. Attributes we are interested in:
11824
- const ATTRIBUTES_TO_RECORD = new Set([
11825
- 'id',
11826
- 'class',
11827
- 'aria-label',
11828
- 'role',
11829
- 'name',
11830
- 'alt',
11831
- 'title',
11832
- 'data-test-id',
11833
- 'data-testid',
11834
- ]);
11835
-
11836
- /**
11837
- * Inclusion list of attributes that we want to record from the DOM element
11838
- */
11839
- function getAttributesToRecord(attributes) {
11840
- const obj = {};
11841
- for (const key in attributes) {
11842
- if (ATTRIBUTES_TO_RECORD.has(key)) {
11843
- let normalizedKey = key;
11844
-
11845
- if (key === 'data-testid' || key === 'data-test-id') {
11846
- normalizedKey = 'testId';
11847
- }
11848
-
11849
- obj[normalizedKey] = attributes[key];
11850
- }
11851
- }
11852
-
11853
- return obj;
11854
- }
11855
-
11856
- const handleDomListener = (
11857
- replay,
11858
- ) => {
11859
- const slowClickExperiment = replay.getOptions()._experiments.slowClicks;
11860
-
11861
- const slowClickConfig = slowClickExperiment
11862
- ? {
11863
- threshold: slowClickExperiment.threshold,
11864
- timeout: slowClickExperiment.timeout,
11865
- scrollTimeout: slowClickExperiment.scrollTimeout,
11866
- ignoreSelector: slowClickExperiment.ignoreSelectors ? slowClickExperiment.ignoreSelectors.join(',') : '',
11867
- }
11868
- : undefined;
11869
-
11870
- return (handlerData) => {
11871
- if (!replay.isEnabled()) {
11872
- return;
11873
- }
11874
-
11875
- const result = handleDom(handlerData);
11876
-
11877
- if (!result) {
11878
- return;
11879
- }
11880
-
11881
- const isClick = handlerData.name === 'click';
11882
- const event = isClick && (handlerData.event );
11883
- // Ignore clicks if ctrl/alt/meta keys are held down as they alter behavior of clicks (e.g. open in new tab)
11884
- if (isClick && slowClickConfig && event && !event.altKey && !event.metaKey && !event.ctrlKey) {
11885
- detectSlowClick(
11886
- replay,
11887
- slowClickConfig,
11888
- result ,
11889
- getClickTargetNode(handlerData.event) ,
11890
- );
11891
- }
11892
-
11893
- addBreadcrumbEvent(replay, result);
11894
- };
11895
- };
11896
-
11897
- /** Get the base DOM breadcrumb. */
11898
- function getBaseDomBreadcrumb(target, message) {
11899
- // `__sn` property is the serialized node created by rrweb
11900
- const serializedNode = target && isRrwebNode(target) && target.__sn.type === NodeType.Element ? target.__sn : null;
11901
-
11902
- return {
11903
- message,
11904
- data: serializedNode
11905
- ? {
11906
- nodeId: serializedNode.id,
11907
- node: {
11908
- id: serializedNode.id,
11909
- tagName: serializedNode.tagName,
11910
- textContent: target
11911
- ? Array.from(target.childNodes)
11912
- .map(
11913
- (node) => '__sn' in node && node.__sn.type === NodeType.Text && node.__sn.textContent,
11914
- )
11915
- .filter(Boolean) // filter out empty values
11916
- .map(text => (text ).trim())
11917
- .join('')
11918
- : '',
11919
- attributes: getAttributesToRecord(serializedNode.attributes),
11920
- },
11921
- }
11922
- : {},
11923
- };
11924
- }
11925
-
11926
- /**
11927
- * An event handler to react to DOM events.
11928
- * Exported for tests.
11929
- */
11930
- function handleDom(handlerData) {
11931
- const { target, message } = getDomTarget(handlerData);
11932
-
11933
- return createBreadcrumb({
11934
- category: `ui.${handlerData.name}`,
11935
- ...getBaseDomBreadcrumb(target, message),
11936
- });
11937
- }
11938
-
11939
- function getDomTarget(handlerData) {
11940
- const isClick = handlerData.name === 'click';
11941
-
11942
- let message;
11943
- let target = null;
11944
-
11945
- // Accessing event.target can throw (see getsentry/raven-js#838, #768)
11946
- try {
11947
- target = isClick ? getClickTargetNode(handlerData.event) : getTargetNode(handlerData.event);
11948
- message = htmlTreeAsString(target, { maxStringLength: 200 }) || '<unknown>';
11949
- } catch (e) {
11950
- message = '<unknown>';
11951
- }
11952
-
11953
- return { target, message };
11954
- }
11955
-
11956
- function isRrwebNode(node) {
11957
- return '__sn' in node;
11958
- }
11959
-
11960
- function getTargetNode(event) {
11961
- if (isEventWithTarget(event)) {
11962
- return event.target ;
11963
- }
11964
-
11965
- return event;
11966
- }
11967
-
11968
- const INTERACTIVE_SELECTOR = 'button,a';
11969
-
11970
- // For clicks, we check if the target is inside of a button or link
11971
- // If so, we use this as the target instead
11972
- // This is useful because if you click on the image in <button><img></button>,
11973
- // The target will be the image, not the button, which we don't want here
11974
- function getClickTargetNode(event) {
11975
- const target = getTargetNode(event);
11976
-
11977
- if (!target || !(target instanceof Element)) {
11978
- return target;
11979
- }
11980
-
11981
- const closestInteractive = target.closest(INTERACTIVE_SELECTOR);
11982
- return closestInteractive || target;
11983
- }
11984
-
11985
- function isEventWithTarget(event) {
11986
- return typeof event === 'object' && !!event && 'target' in event;
11987
- }
11988
-
11989
- /** Handle keyboard events & create breadcrumbs. */
11990
- function handleKeyboardEvent(replay, event) {
11991
- if (!replay.isEnabled()) {
11992
- return;
11993
- }
11994
-
11995
- replay.triggerUserActivity();
11996
-
11997
- const breadcrumb = getKeyboardBreadcrumb(event);
11998
-
11999
- if (!breadcrumb) {
12000
- return;
12001
- }
12002
-
12003
- addBreadcrumbEvent(replay, breadcrumb);
12004
- }
12005
-
12006
- /** exported only for tests */
12007
- function getKeyboardBreadcrumb(event) {
12008
- const { metaKey, shiftKey, ctrlKey, altKey, key, target } = event;
12009
-
12010
- // never capture for input fields
12011
- if (!target || isInputElement(target )) {
12012
- return null;
12013
- }
12014
-
12015
- // Note: We do not consider shift here, as that means "uppercase"
12016
- const hasModifierKey = metaKey || ctrlKey || altKey;
12017
- const isCharacterKey = key.length === 1; // other keys like Escape, Tab, etc have a longer length
12018
-
12019
- // Do not capture breadcrumb if only a word key is pressed
12020
- // This could leak e.g. user input
12021
- if (!hasModifierKey && isCharacterKey) {
12022
- return null;
12023
- }
12024
-
12025
- const message = htmlTreeAsString(target, { maxStringLength: 200 }) || '<unknown>';
12026
- const baseBreadcrumb = getBaseDomBreadcrumb(target , message);
12027
-
12028
- return createBreadcrumb({
12029
- category: 'ui.keyDown',
12030
- message,
12031
- data: {
12032
- ...baseBreadcrumb.data,
12033
- metaKey,
12034
- shiftKey,
12035
- ctrlKey,
12036
- altKey,
12037
- key,
12038
- },
12039
- });
12040
- }
12041
-
12042
- function isInputElement(target) {
12043
- return target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable;
12044
- }
12045
-
12046
11346
  const NAVIGATION_ENTRY_KEYS = [
12047
11347
  'name',
12048
11348
  'type',
@@ -12196,19 +11496,20 @@ class EventBufferArray {
12196
11496
  return this.events.length > 0;
12197
11497
  }
12198
11498
 
12199
- /** @inheritdoc */
12200
- get type() {
12201
- return 'sync';
12202
- }
12203
-
12204
11499
  /** @inheritdoc */
12205
11500
  destroy() {
12206
11501
  this.events = [];
12207
11502
  }
12208
11503
 
12209
11504
  /** @inheritdoc */
12210
- async addEvent(event) {
11505
+ async addEvent(event, isCheckout) {
11506
+ if (isCheckout) {
11507
+ this.events = [event];
11508
+ return;
11509
+ }
11510
+
12211
11511
  this.events.push(event);
11512
+ return;
12212
11513
  }
12213
11514
 
12214
11515
  /** @inheritdoc */
@@ -12222,22 +11523,6 @@ class EventBufferArray {
12222
11523
  resolve(JSON.stringify(eventsRet));
12223
11524
  });
12224
11525
  }
12225
-
12226
- /** @inheritdoc */
12227
- clear() {
12228
- this.events = [];
12229
- }
12230
-
12231
- /** @inheritdoc */
12232
- getEarliestTimestamp() {
12233
- const timestamp = this.events.map(event => event.timestamp).sort()[0];
12234
-
12235
- if (!timestamp) {
12236
- return null;
12237
- }
12238
-
12239
- return timestampToMs(timestamp);
12240
- }
12241
11526
  }
12242
11527
 
12243
11528
  /**
@@ -12345,20 +11630,11 @@ class WorkerHandler {
12345
11630
  * Exported only for testing.
12346
11631
  */
12347
11632
  class EventBufferCompressionWorker {
11633
+ /** @inheritdoc */
12348
11634
 
12349
11635
  constructor(worker) {
12350
11636
  this._worker = new WorkerHandler(worker);
12351
- this._earliestTimestamp = null;
12352
- }
12353
-
12354
- /** @inheritdoc */
12355
- get hasEvents() {
12356
- return !!this._earliestTimestamp;
12357
- }
12358
-
12359
- /** @inheritdoc */
12360
- get type() {
12361
- return 'worker';
11637
+ this.hasEvents = false;
12362
11638
  }
12363
11639
 
12364
11640
  /**
@@ -12381,10 +11657,13 @@ class EventBufferCompressionWorker {
12381
11657
  *
12382
11658
  * Returns true if event was successfuly received and processed by worker.
12383
11659
  */
12384
- addEvent(event) {
12385
- const timestamp = timestampToMs(event.timestamp);
12386
- if (!this._earliestTimestamp || timestamp < this._earliestTimestamp) {
12387
- this._earliestTimestamp = timestamp;
11660
+ async addEvent(event, isCheckout) {
11661
+ this.hasEvents = true;
11662
+
11663
+ if (isCheckout) {
11664
+ // This event is a checkout, make sure worker buffer is cleared before
11665
+ // proceeding.
11666
+ await this._clear();
12388
11667
  }
12389
11668
 
12390
11669
  return this._sendEventToWorker(event);
@@ -12397,18 +11676,6 @@ class EventBufferCompressionWorker {
12397
11676
  return this._finishRequest();
12398
11677
  }
12399
11678
 
12400
- /** @inheritdoc */
12401
- clear() {
12402
- this._earliestTimestamp = null;
12403
- // We do not wait on this, as we assume the order of messages is consistent for the worker
12404
- void this._worker.postMessage('clear');
12405
- }
12406
-
12407
- /** @inheritdoc */
12408
- getEarliestTimestamp() {
12409
- return this._earliestTimestamp;
12410
- }
12411
-
12412
11679
  /**
12413
11680
  * Send the event to the worker.
12414
11681
  */
@@ -12422,10 +11689,15 @@ class EventBufferCompressionWorker {
12422
11689
  async _finishRequest() {
12423
11690
  const response = await this._worker.postMessage('finish');
12424
11691
 
12425
- this._earliestTimestamp = null;
11692
+ this.hasEvents = false;
12426
11693
 
12427
11694
  return response;
12428
11695
  }
11696
+
11697
+ /** Clear any pending events from the worker. */
11698
+ _clear() {
11699
+ return this._worker.postMessage('clear');
11700
+ }
12429
11701
  }
12430
11702
 
12431
11703
  /**
@@ -12443,11 +11715,6 @@ class EventBufferProxy {
12443
11715
  this._ensureWorkerIsLoadedPromise = this._ensureWorkerIsLoaded();
12444
11716
  }
12445
11717
 
12446
- /** @inheritdoc */
12447
- get type() {
12448
- return this._used.type;
12449
- }
12450
-
12451
11718
  /** @inheritDoc */
12452
11719
  get hasEvents() {
12453
11720
  return this._used.hasEvents;
@@ -12459,23 +11726,13 @@ class EventBufferProxy {
12459
11726
  this._compression.destroy();
12460
11727
  }
12461
11728
 
12462
- /** @inheritdoc */
12463
- clear() {
12464
- return this._used.clear();
12465
- }
12466
-
12467
- /** @inheritdoc */
12468
- getEarliestTimestamp() {
12469
- return this._used.getEarliestTimestamp();
12470
- }
12471
-
12472
11729
  /**
12473
11730
  * Add an event to the event buffer.
12474
11731
  *
12475
11732
  * Returns true if event was successfully added.
12476
11733
  */
12477
- addEvent(event) {
12478
- return this._used.addEvent(event);
11734
+ addEvent(event, isCheckout) {
11735
+ return this._used.addEvent(event, isCheckout);
12479
11736
  }
12480
11737
 
12481
11738
  /** @inheritDoc */
@@ -12550,31 +11807,6 @@ function createEventBuffer({ useCompression }) {
12550
11807
  return new EventBufferArray();
12551
11808
  }
12552
11809
 
12553
- /**
12554
- * Removes the session from Session Storage and unsets session in replay instance
12555
- */
12556
- function clearSession(replay) {
12557
- deleteSession();
12558
- replay.session = undefined;
12559
- }
12560
-
12561
- /**
12562
- * Deletes a session from storage
12563
- */
12564
- function deleteSession() {
12565
- const hasSessionStorage = 'sessionStorage' in WINDOW;
12566
-
12567
- if (!hasSessionStorage) {
12568
- return;
12569
- }
12570
-
12571
- try {
12572
- WINDOW.sessionStorage.removeItem(REPLAY_SESSION_KEY);
12573
- } catch (e) {
12574
- // Ignore potential SecurityError exceptions
12575
- }
12576
- }
12577
-
12578
11810
  /**
12579
11811
  * Given an initial timestamp and an expiry duration, checks to see if current
12580
11812
  * time should be considered as expired.
@@ -12605,26 +11837,11 @@ function isSessionExpired(session, timeouts, targetTime = +new Date()) {
12605
11837
  // First, check that maximum session length has not been exceeded
12606
11838
  isExpired(session.started, timeouts.maxSessionLife, targetTime) ||
12607
11839
  // check that the idle timeout has not been exceeded (i.e. user has
12608
- // performed an action within the last `sessionIdleExpire` ms)
12609
- isExpired(session.lastActivity, timeouts.sessionIdleExpire, targetTime)
11840
+ // performed an action within the last `idleTimeout` ms)
11841
+ isExpired(session.lastActivity, timeouts.sessionIdle, targetTime)
12610
11842
  );
12611
11843
  }
12612
11844
 
12613
- /**
12614
- * Given a sample rate, returns true if replay should be sampled.
12615
- *
12616
- * 1.0 = 100% sampling
12617
- * 0.0 = 0% sampling
12618
- */
12619
- function isSampled(sampleRate) {
12620
- if (sampleRate === undefined) {
12621
- return false;
12622
- }
12623
-
12624
- // Math.random() returns a number in range of 0 to 1 (inclusive of 0, but not 1)
12625
- return Math.random() < sampleRate;
12626
- }
12627
-
12628
11845
  /**
12629
11846
  * Save a session to session storage.
12630
11847
  */
@@ -12641,6 +11858,21 @@ function saveSession(session) {
12641
11858
  }
12642
11859
  }
12643
11860
 
11861
+ /**
11862
+ * Given a sample rate, returns true if replay should be sampled.
11863
+ *
11864
+ * 1.0 = 100% sampling
11865
+ * 0.0 = 0% sampling
11866
+ */
11867
+ function isSampled(sampleRate) {
11868
+ if (sampleRate === undefined) {
11869
+ return false;
11870
+ }
11871
+
11872
+ // Math.random() returns a number in range of 0 to 1 (inclusive of 0, but not 1)
11873
+ return Math.random() < sampleRate;
11874
+ }
11875
+
12644
11876
  /**
12645
11877
  * Get a session with defaults & applied sampling.
12646
11878
  */
@@ -12659,15 +11891,14 @@ function makeSession(session) {
12659
11891
  lastActivity,
12660
11892
  segmentId,
12661
11893
  sampled,
12662
- shouldRefresh: true,
12663
11894
  };
12664
11895
  }
12665
11896
 
12666
11897
  /**
12667
11898
  * Get the sampled status for a session based on sample rates & current sampled status.
12668
11899
  */
12669
- function getSessionSampleType(sessionSampleRate, allowBuffering) {
12670
- return isSampled(sessionSampleRate) ? 'session' : allowBuffering ? 'buffer' : false;
11900
+ function getSessionSampleType(sessionSampleRate, errorSampleRate) {
11901
+ return isSampled(sessionSampleRate) ? 'session' : isSampled(errorSampleRate) ? 'error' : false;
12671
11902
  }
12672
11903
 
12673
11904
  /**
@@ -12675,8 +11906,8 @@ function getSessionSampleType(sessionSampleRate, allowBuffering) {
12675
11906
  * that all replays will be saved to as attachments. Currently, we only expect
12676
11907
  * one of these Sentry events per "replay session".
12677
11908
  */
12678
- function createSession({ sessionSampleRate, allowBuffering, stickySession = false }) {
12679
- const sampled = getSessionSampleType(sessionSampleRate, allowBuffering);
11909
+ function createSession({ sessionSampleRate, errorSampleRate, stickySession = false }) {
11910
+ const sampled = getSessionSampleType(sessionSampleRate, errorSampleRate);
12680
11911
  const session = makeSession({
12681
11912
  sampled,
12682
11913
  });
@@ -12724,7 +11955,7 @@ function getSession({
12724
11955
  currentSession,
12725
11956
  stickySession,
12726
11957
  sessionSampleRate,
12727
- allowBuffering,
11958
+ errorSampleRate,
12728
11959
  }) {
12729
11960
  // If session exists and is passed, use it instead of always hitting session storage
12730
11961
  const session = currentSession || (stickySession && fetchSession());
@@ -12737,9 +11968,8 @@ function getSession({
12737
11968
 
12738
11969
  if (!isExpired) {
12739
11970
  return { type: 'saved', session };
12740
- } else if (!session.shouldRefresh) {
12741
- // In this case, stop
12742
- // This is the case if we have an error session that is completed (=triggered an error)
11971
+ } else if (session.sampled === 'error') {
11972
+ // Error samples should not be re-created when expired, but instead we stop when the replay is done
12743
11973
  const discardedSession = makeSession({ sampled: false });
12744
11974
  return { type: 'new', session: discardedSession };
12745
11975
  } else {
@@ -12751,12 +11981,65 @@ function getSession({
12751
11981
  const newSession = createSession({
12752
11982
  stickySession,
12753
11983
  sessionSampleRate,
12754
- allowBuffering,
11984
+ errorSampleRate,
12755
11985
  });
12756
11986
 
12757
11987
  return { type: 'new', session: newSession };
12758
11988
  }
12759
11989
 
11990
+ /**
11991
+ * Add an event to the event buffer.
11992
+ * `isCheckout` is true if this is either the very first event, or an event triggered by `checkoutEveryNms`.
11993
+ */
11994
+ async function addEvent(
11995
+ replay,
11996
+ event,
11997
+ isCheckout,
11998
+ ) {
11999
+ if (!replay.eventBuffer) {
12000
+ // This implies that `_isEnabled` is false
12001
+ return null;
12002
+ }
12003
+
12004
+ if (replay.isPaused()) {
12005
+ // Do not add to event buffer when recording is paused
12006
+ return null;
12007
+ }
12008
+
12009
+ // TODO: sadness -- we will want to normalize timestamps to be in ms -
12010
+ // requires coordination with frontend
12011
+ const isMs = event.timestamp > 9999999999;
12012
+ const timestampInMs = isMs ? event.timestamp : event.timestamp * 1000;
12013
+
12014
+ // Throw out events that happen more than 5 minutes ago. This can happen if
12015
+ // page has been left open and idle for a long period of time and user
12016
+ // comes back to trigger a new session. The performance entries rely on
12017
+ // `performance.timeOrigin`, which is when the page first opened.
12018
+ if (timestampInMs + replay.timeouts.sessionIdle < Date.now()) {
12019
+ return null;
12020
+ }
12021
+
12022
+ // Only record earliest event if a new session was created, otherwise it
12023
+ // shouldn't be relevant
12024
+ const earliestEvent = replay.getContext().earliestEvent;
12025
+ if (replay.session && replay.session.segmentId === 0 && (!earliestEvent || timestampInMs < earliestEvent)) {
12026
+ replay.getContext().earliestEvent = timestampInMs;
12027
+ }
12028
+
12029
+ try {
12030
+ return await replay.eventBuffer.addEvent(event, isCheckout);
12031
+ } catch (error) {
12032
+ (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.error(error);
12033
+ replay.stop('addEvent');
12034
+
12035
+ const client = getCurrentHub().getClient();
12036
+
12037
+ if (client) {
12038
+ client.recordDroppedEvent('internal_sdk_error', 'replay');
12039
+ }
12040
+ }
12041
+ }
12042
+
12760
12043
  /** If the event is an error event */
12761
12044
  function isErrorEvent(event) {
12762
12045
  return !event.type;
@@ -12806,21 +12089,31 @@ function handleAfterSendEvent(replay) {
12806
12089
  return;
12807
12090
  }
12808
12091
 
12809
- // Add error to list of errorIds of replay. This is ok to do even if not
12810
- // sampled because context will get reset at next checkout.
12811
- // XXX: There is also a race condition where it's possible to capture an
12812
- // error to Sentry before Replay SDK has loaded, but response returns after
12813
- // it was loaded, and this gets called.
12092
+ // Add error to list of errorIds of replay
12814
12093
  if (event.event_id) {
12815
12094
  replay.getContext().errorIds.add(event.event_id);
12816
12095
  }
12817
12096
 
12818
- // If error event is tagged with replay id it means it was sampled (when in buffer mode)
12097
+ // Trigger error recording
12819
12098
  // Need to be very careful that this does not cause an infinite loop
12820
- if (replay.recordingMode === 'buffer' && event.tags && event.tags.replayId) {
12821
- setTimeout(() => {
12822
- // Capture current event buffer as new replay
12823
- void replay.sendBufferedReplayOrFlush();
12099
+ if (
12100
+ replay.recordingMode === 'error' &&
12101
+ event.exception &&
12102
+ event.message !== UNABLE_TO_SEND_REPLAY // ignore this error because otherwise we could loop indefinitely with trying to capture replay and failing
12103
+ ) {
12104
+ setTimeout(async () => {
12105
+ // Allow flush to complete before resuming as a session recording, otherwise
12106
+ // the checkout from `startRecording` may be included in the payload.
12107
+ // Prefer to keep the error replay as a separate (and smaller) segment
12108
+ // than the session replay.
12109
+ await replay.flushImmediate();
12110
+
12111
+ if (replay.stopRecording()) {
12112
+ // Reset all "capture on error" configuration before
12113
+ // starting a new recording
12114
+ replay.recordingMode = 'session';
12115
+ replay.startRecording();
12116
+ }
12824
12117
  });
12825
12118
  }
12826
12119
  };
@@ -12842,6 +12135,166 @@ function isBaseTransportSend() {
12842
12135
  );
12843
12136
  }
12844
12137
 
12138
+ var NodeType;
12139
+ (function (NodeType) {
12140
+ NodeType[NodeType["Document"] = 0] = "Document";
12141
+ NodeType[NodeType["DocumentType"] = 1] = "DocumentType";
12142
+ NodeType[NodeType["Element"] = 2] = "Element";
12143
+ NodeType[NodeType["Text"] = 3] = "Text";
12144
+ NodeType[NodeType["CDATA"] = 4] = "CDATA";
12145
+ NodeType[NodeType["Comment"] = 5] = "Comment";
12146
+ })(NodeType || (NodeType = {}));
12147
+
12148
+ /**
12149
+ * Create a breadcrumb for a replay.
12150
+ */
12151
+ function createBreadcrumb(
12152
+ breadcrumb,
12153
+ ) {
12154
+ return {
12155
+ timestamp: Date.now() / 1000,
12156
+ type: 'default',
12157
+ ...breadcrumb,
12158
+ };
12159
+ }
12160
+
12161
+ /**
12162
+ * Add a breadcrumb event to replay.
12163
+ */
12164
+ function addBreadcrumbEvent(replay, breadcrumb) {
12165
+ if (breadcrumb.category === 'sentry.transaction') {
12166
+ return;
12167
+ }
12168
+
12169
+ if (['ui.click', 'ui.input'].includes(breadcrumb.category )) {
12170
+ replay.triggerUserActivity();
12171
+ } else {
12172
+ replay.checkAndHandleExpiredSession();
12173
+ }
12174
+
12175
+ replay.addUpdate(() => {
12176
+ void addEvent(replay, {
12177
+ type: EventType.Custom,
12178
+ // TODO: We were converting from ms to seconds for breadcrumbs, spans,
12179
+ // but maybe we should just keep them as milliseconds
12180
+ timestamp: (breadcrumb.timestamp || 0) * 1000,
12181
+ data: {
12182
+ tag: 'breadcrumb',
12183
+ payload: breadcrumb,
12184
+ },
12185
+ });
12186
+
12187
+ // Do not flush after console log messages
12188
+ return breadcrumb.category === 'console';
12189
+ });
12190
+ }
12191
+
12192
+ // Note that these are the serialized attributes and not attributes directly on
12193
+ // the DOM Node. Attributes we are interested in:
12194
+ const ATTRIBUTES_TO_RECORD = new Set([
12195
+ 'id',
12196
+ 'class',
12197
+ 'aria-label',
12198
+ 'role',
12199
+ 'name',
12200
+ 'alt',
12201
+ 'title',
12202
+ 'data-test-id',
12203
+ 'data-testid',
12204
+ ]);
12205
+
12206
+ /**
12207
+ * Inclusion list of attributes that we want to record from the DOM element
12208
+ */
12209
+ function getAttributesToRecord(attributes) {
12210
+ const obj = {};
12211
+ for (const key in attributes) {
12212
+ if (ATTRIBUTES_TO_RECORD.has(key)) {
12213
+ let normalizedKey = key;
12214
+
12215
+ if (key === 'data-testid' || key === 'data-test-id') {
12216
+ normalizedKey = 'testId';
12217
+ }
12218
+
12219
+ obj[normalizedKey] = attributes[key];
12220
+ }
12221
+ }
12222
+
12223
+ return obj;
12224
+ }
12225
+
12226
+ const handleDomListener =
12227
+ (replay) =>
12228
+ (handlerData) => {
12229
+ if (!replay.isEnabled()) {
12230
+ return;
12231
+ }
12232
+
12233
+ const result = handleDom(handlerData);
12234
+
12235
+ if (!result) {
12236
+ return;
12237
+ }
12238
+
12239
+ addBreadcrumbEvent(replay, result);
12240
+ };
12241
+
12242
+ /**
12243
+ * An event handler to react to DOM events.
12244
+ */
12245
+ function handleDom(handlerData) {
12246
+ let target;
12247
+ let targetNode;
12248
+
12249
+ // Accessing event.target can throw (see getsentry/raven-js#838, #768)
12250
+ try {
12251
+ targetNode = getTargetNode(handlerData);
12252
+ target = htmlTreeAsString(targetNode);
12253
+ } catch (e) {
12254
+ target = '<unknown>';
12255
+ }
12256
+
12257
+ // `__sn` property is the serialized node created by rrweb
12258
+ const serializedNode =
12259
+ targetNode && '__sn' in targetNode && targetNode.__sn.type === NodeType.Element ? targetNode.__sn : null;
12260
+
12261
+ return createBreadcrumb({
12262
+ category: `ui.${handlerData.name}`,
12263
+ message: target,
12264
+ data: serializedNode
12265
+ ? {
12266
+ nodeId: serializedNode.id,
12267
+ node: {
12268
+ id: serializedNode.id,
12269
+ tagName: serializedNode.tagName,
12270
+ textContent: targetNode
12271
+ ? Array.from(targetNode.childNodes)
12272
+ .map(
12273
+ (node) => '__sn' in node && node.__sn.type === NodeType.Text && node.__sn.textContent,
12274
+ )
12275
+ .filter(Boolean) // filter out empty values
12276
+ .map(text => (text ).trim())
12277
+ .join('')
12278
+ : '',
12279
+ attributes: getAttributesToRecord(serializedNode.attributes),
12280
+ },
12281
+ }
12282
+ : {},
12283
+ });
12284
+ }
12285
+
12286
+ function getTargetNode(handlerData) {
12287
+ if (isEventWithTarget(handlerData.event)) {
12288
+ return handlerData.event.target;
12289
+ }
12290
+
12291
+ return handlerData.event;
12292
+ }
12293
+
12294
+ function isEventWithTarget(event) {
12295
+ return !!(event ).target;
12296
+ }
12297
+
12845
12298
  /**
12846
12299
  * Returns true if we think the given event is an error originating inside of rrweb.
12847
12300
  */
@@ -12865,30 +12318,6 @@ function isRrwebError(event, hint) {
12865
12318
  });
12866
12319
  }
12867
12320
 
12868
- /**
12869
- * Determine if event should be sampled (only applies in buffer mode).
12870
- * When an event is captured by `hanldleGlobalEvent`, when in buffer mode
12871
- * we determine if we want to sample the error or not.
12872
- */
12873
- function shouldSampleForBufferEvent(replay, event) {
12874
- if (replay.recordingMode !== 'buffer') {
12875
- return false;
12876
- }
12877
-
12878
- // ignore this error because otherwise we could loop indefinitely with
12879
- // trying to capture replay and failing
12880
- if (event.message === UNABLE_TO_SEND_REPLAY) {
12881
- return false;
12882
- }
12883
-
12884
- // Require the event to be an error event & to have an exception
12885
- if (!event.exception || event.type) {
12886
- return false;
12887
- }
12888
-
12889
- return isSampled(replay.getOptions().errorSampleRate);
12890
- }
12891
-
12892
12321
  /**
12893
12322
  * Returns a listener to be added to `addGlobalEventProcessor(listener)`.
12894
12323
  */
@@ -12918,16 +12347,8 @@ function handleGlobalEventListener(
12918
12347
  return null;
12919
12348
  }
12920
12349
 
12921
- // When in buffer mode, we decide to sample here.
12922
- // Later, in `handleAfterSendEvent`, if the replayId is set, we know that we sampled
12923
- // And convert the buffer session to a full session
12924
- const isErrorEventSampled = shouldSampleForBufferEvent(replay, event);
12925
-
12926
- // Tag errors if it has been sampled in buffer mode, or if it is session mode
12927
- // Only tag transactions if in session mode
12928
- const shouldTagReplayId = isErrorEventSampled || replay.recordingMode === 'session';
12929
-
12930
- if (shouldTagReplayId) {
12350
+ // Only tag transactions with replayId if not waiting for an error
12351
+ if (isErrorEvent(event) || (isTransactionEvent(event) && replay.recordingMode === 'session')) {
12931
12352
  event.tags = { ...event.tags, replayId: replay.getSessionId() };
12932
12353
  }
12933
12354
 
@@ -12977,7 +12398,7 @@ function createPerformanceSpans(
12977
12398
  ) {
12978
12399
  return entries.map(({ type, start, end, name, data }) =>
12979
12400
  addEvent(replay, {
12980
- type: EventType$1.Custom,
12401
+ type: EventType.Custom,
12981
12402
  timestamp: start,
12982
12403
  data: {
12983
12404
  tag: 'performanceSpan',
@@ -13126,14 +12547,12 @@ function handleFetchSpanListener(replay) {
13126
12547
  function handleXhr(handlerData) {
13127
12548
  const { startTimestamp, endTimestamp, xhr } = handlerData;
13128
12549
 
13129
- const sentryXhrData = xhr[SENTRY_XHR_DATA_KEY];
13130
-
13131
- if (!startTimestamp || !endTimestamp || !sentryXhrData) {
12550
+ if (!startTimestamp || !endTimestamp || !xhr.__sentry_xhr__) {
13132
12551
  return null;
13133
12552
  }
13134
12553
 
13135
12554
  // This is only used as a fallback, so we know the body sizes are never set here
13136
- const { method, url, status_code: statusCode } = sentryXhrData;
12555
+ const { method, url, status_code: statusCode } = xhr.__sentry_xhr__;
13137
12556
 
13138
12557
  if (url === undefined) {
13139
12558
  return null;
@@ -13166,393 +12585,6 @@ function handleXhrSpanListener(replay) {
13166
12585
  };
13167
12586
  }
13168
12587
 
13169
- const OBJ = 10;
13170
- const OBJ_KEY = 11;
13171
- const OBJ_KEY_STR = 12;
13172
- const OBJ_VAL = 13;
13173
- const OBJ_VAL_STR = 14;
13174
- const OBJ_VAL_COMPLETED = 15;
13175
-
13176
- const ARR = 20;
13177
- const ARR_VAL = 21;
13178
- const ARR_VAL_STR = 22;
13179
- const ARR_VAL_COMPLETED = 23;
13180
-
13181
- const ALLOWED_PRIMITIVES = ['true', 'false', 'null'];
13182
-
13183
- /**
13184
- * Complete an incomplete JSON string.
13185
- * This will ensure that the last element always has a `"~~"` to indicate it was truncated.
13186
- * For example, `[1,2,` will be completed to `[1,2,"~~"]`
13187
- * and `{"aa":"b` will be completed to `{"aa":"b~~"}`
13188
- */
13189
- function completeJson(incompleteJson, stack) {
13190
- if (!stack.length) {
13191
- return incompleteJson;
13192
- }
13193
-
13194
- let json = incompleteJson;
13195
-
13196
- // Most checks are only needed for the last step in the stack
13197
- const lastPos = stack.length - 1;
13198
- const lastStep = stack[lastPos];
13199
-
13200
- json = _fixLastStep(json, lastStep);
13201
-
13202
- // Complete remaining steps - just add closing brackets
13203
- for (let i = lastPos; i >= 0; i--) {
13204
- const step = stack[i];
13205
-
13206
- switch (step) {
13207
- case OBJ:
13208
- json = `${json}}`;
13209
- break;
13210
- case ARR:
13211
- json = `${json}]`;
13212
- break;
13213
- }
13214
- }
13215
-
13216
- return json;
13217
- }
13218
-
13219
- function _fixLastStep(json, lastStep) {
13220
- switch (lastStep) {
13221
- // Object cases
13222
- case OBJ:
13223
- return `${json}"~~":"~~"`;
13224
- case OBJ_KEY:
13225
- return `${json}:"~~"`;
13226
- case OBJ_KEY_STR:
13227
- return `${json}~~":"~~"`;
13228
- case OBJ_VAL:
13229
- return _maybeFixIncompleteObjValue(json);
13230
- case OBJ_VAL_STR:
13231
- return `${json}~~"`;
13232
- case OBJ_VAL_COMPLETED:
13233
- return `${json},"~~":"~~"`;
13234
-
13235
- // Array cases
13236
- case ARR:
13237
- return `${json}"~~"`;
13238
- case ARR_VAL:
13239
- return _maybeFixIncompleteArrValue(json);
13240
- case ARR_VAL_STR:
13241
- return `${json}~~"`;
13242
- case ARR_VAL_COMPLETED:
13243
- return `${json},"~~"`;
13244
- }
13245
-
13246
- return json;
13247
- }
13248
-
13249
- function _maybeFixIncompleteArrValue(json) {
13250
- const pos = _findLastArrayDelimiter(json);
13251
-
13252
- if (pos > -1) {
13253
- const part = json.slice(pos + 1);
13254
-
13255
- if (ALLOWED_PRIMITIVES.includes(part.trim())) {
13256
- return `${json},"~~"`;
13257
- }
13258
-
13259
- // Everything else is replaced with `"~~"`
13260
- return `${json.slice(0, pos + 1)}"~~"`;
13261
- }
13262
-
13263
- // fallback, this shouldn't happen, to be save
13264
- return json;
13265
- }
13266
-
13267
- function _findLastArrayDelimiter(json) {
13268
- for (let i = json.length - 1; i >= 0; i--) {
13269
- const char = json[i];
13270
-
13271
- if (char === ',' || char === '[') {
13272
- return i;
13273
- }
13274
- }
13275
-
13276
- return -1;
13277
- }
13278
-
13279
- function _maybeFixIncompleteObjValue(json) {
13280
- const startPos = json.lastIndexOf(':');
13281
-
13282
- const part = json.slice(startPos + 1);
13283
-
13284
- if (ALLOWED_PRIMITIVES.includes(part.trim())) {
13285
- return `${json},"~~":"~~"`;
13286
- }
13287
-
13288
- // Everything else is replaced with `"~~"`
13289
- // This also means we do not have incomplete numbers, e.g `[1` is replaced with `["~~"]`
13290
- return `${json.slice(0, startPos + 1)}"~~"`;
13291
- }
13292
-
13293
- /**
13294
- * Evaluate an (incomplete) JSON string.
13295
- */
13296
- function evaluateJson(json) {
13297
- const stack = [];
13298
-
13299
- for (let pos = 0; pos < json.length; pos++) {
13300
- _evaluateJsonPos(stack, json, pos);
13301
- }
13302
-
13303
- return stack;
13304
- }
13305
-
13306
- function _evaluateJsonPos(stack, json, pos) {
13307
- const curStep = stack[stack.length - 1];
13308
-
13309
- const char = json[pos];
13310
-
13311
- const whitespaceRegex = /\s/;
13312
-
13313
- if (whitespaceRegex.test(char)) {
13314
- return;
13315
- }
13316
-
13317
- if (char === '"' && !_isEscaped(json, pos)) {
13318
- _handleQuote(stack, curStep);
13319
- return;
13320
- }
13321
-
13322
- switch (char) {
13323
- case '{':
13324
- _handleObj(stack, curStep);
13325
- break;
13326
- case '[':
13327
- _handleArr(stack, curStep);
13328
- break;
13329
- case ':':
13330
- _handleColon(stack, curStep);
13331
- break;
13332
- case ',':
13333
- _handleComma(stack, curStep);
13334
- break;
13335
- case '}':
13336
- _handleObjClose(stack, curStep);
13337
- break;
13338
- case ']':
13339
- _handleArrClose(stack, curStep);
13340
- break;
13341
- }
13342
- }
13343
-
13344
- function _handleQuote(stack, curStep) {
13345
- // End of obj value
13346
- if (curStep === OBJ_VAL_STR) {
13347
- stack.pop();
13348
- stack.push(OBJ_VAL_COMPLETED);
13349
- return;
13350
- }
13351
-
13352
- // End of arr value
13353
- if (curStep === ARR_VAL_STR) {
13354
- stack.pop();
13355
- stack.push(ARR_VAL_COMPLETED);
13356
- return;
13357
- }
13358
-
13359
- // Start of obj value
13360
- if (curStep === OBJ_VAL) {
13361
- stack.push(OBJ_VAL_STR);
13362
- return;
13363
- }
13364
-
13365
- // Start of arr value
13366
- if (curStep === ARR_VAL) {
13367
- stack.push(ARR_VAL_STR);
13368
- return;
13369
- }
13370
-
13371
- // Start of obj key
13372
- if (curStep === OBJ) {
13373
- stack.push(OBJ_KEY_STR);
13374
- return;
13375
- }
13376
-
13377
- // End of obj key
13378
- if (curStep === OBJ_KEY_STR) {
13379
- stack.pop();
13380
- stack.push(OBJ_KEY);
13381
- return;
13382
- }
13383
- }
13384
-
13385
- function _handleObj(stack, curStep) {
13386
- // Initial object
13387
- if (!curStep) {
13388
- stack.push(OBJ);
13389
- return;
13390
- }
13391
-
13392
- // New object as obj value
13393
- if (curStep === OBJ_VAL) {
13394
- stack.push(OBJ);
13395
- return;
13396
- }
13397
-
13398
- // New object as array element
13399
- if (curStep === ARR_VAL) {
13400
- stack.push(OBJ);
13401
- }
13402
-
13403
- // New object as first array element
13404
- if (curStep === ARR) {
13405
- stack.push(OBJ);
13406
- return;
13407
- }
13408
- }
13409
-
13410
- function _handleArr(stack, curStep) {
13411
- // Initial array
13412
- if (!curStep) {
13413
- stack.push(ARR);
13414
- stack.push(ARR_VAL);
13415
- return;
13416
- }
13417
-
13418
- // New array as obj value
13419
- if (curStep === OBJ_VAL) {
13420
- stack.push(ARR);
13421
- stack.push(ARR_VAL);
13422
- return;
13423
- }
13424
-
13425
- // New array as array element
13426
- if (curStep === ARR_VAL) {
13427
- stack.push(ARR);
13428
- stack.push(ARR_VAL);
13429
- }
13430
-
13431
- // New array as first array element
13432
- if (curStep === ARR) {
13433
- stack.push(ARR);
13434
- stack.push(ARR_VAL);
13435
- return;
13436
- }
13437
- }
13438
-
13439
- function _handleColon(stack, curStep) {
13440
- if (curStep === OBJ_KEY) {
13441
- stack.pop();
13442
- stack.push(OBJ_VAL);
13443
- }
13444
- }
13445
-
13446
- function _handleComma(stack, curStep) {
13447
- // Comma after obj value
13448
- if (curStep === OBJ_VAL) {
13449
- stack.pop();
13450
- return;
13451
- }
13452
- if (curStep === OBJ_VAL_COMPLETED) {
13453
- // Pop OBJ_VAL_COMPLETED & OBJ_VAL
13454
- stack.pop();
13455
- stack.pop();
13456
- return;
13457
- }
13458
-
13459
- // Comma after arr value
13460
- if (curStep === ARR_VAL) {
13461
- // do nothing - basically we'd pop ARR_VAL but add it right back
13462
- return;
13463
- }
13464
-
13465
- if (curStep === ARR_VAL_COMPLETED) {
13466
- // Pop ARR_VAL_COMPLETED
13467
- stack.pop();
13468
-
13469
- // basically we'd pop ARR_VAL but add it right back
13470
- return;
13471
- }
13472
- }
13473
-
13474
- function _handleObjClose(stack, curStep) {
13475
- // Empty object {}
13476
- if (curStep === OBJ) {
13477
- stack.pop();
13478
- }
13479
-
13480
- // Object with element
13481
- if (curStep === OBJ_VAL) {
13482
- // Pop OBJ_VAL, OBJ
13483
- stack.pop();
13484
- stack.pop();
13485
- }
13486
-
13487
- // Obj with element
13488
- if (curStep === OBJ_VAL_COMPLETED) {
13489
- // Pop OBJ_VAL_COMPLETED, OBJ_VAL, OBJ
13490
- stack.pop();
13491
- stack.pop();
13492
- stack.pop();
13493
- }
13494
-
13495
- // if was obj value, complete it
13496
- if (stack[stack.length - 1] === OBJ_VAL) {
13497
- stack.push(OBJ_VAL_COMPLETED);
13498
- }
13499
-
13500
- // if was arr value, complete it
13501
- if (stack[stack.length - 1] === ARR_VAL) {
13502
- stack.push(ARR_VAL_COMPLETED);
13503
- }
13504
- }
13505
-
13506
- function _handleArrClose(stack, curStep) {
13507
- // Empty array []
13508
- if (curStep === ARR) {
13509
- stack.pop();
13510
- }
13511
-
13512
- // Array with element
13513
- if (curStep === ARR_VAL) {
13514
- // Pop ARR_VAL, ARR
13515
- stack.pop();
13516
- stack.pop();
13517
- }
13518
-
13519
- // Array with element
13520
- if (curStep === ARR_VAL_COMPLETED) {
13521
- // Pop ARR_VAL_COMPLETED, ARR_VAL, ARR
13522
- stack.pop();
13523
- stack.pop();
13524
- stack.pop();
13525
- }
13526
-
13527
- // if was obj value, complete it
13528
- if (stack[stack.length - 1] === OBJ_VAL) {
13529
- stack.push(OBJ_VAL_COMPLETED);
13530
- }
13531
-
13532
- // if was arr value, complete it
13533
- if (stack[stack.length - 1] === ARR_VAL) {
13534
- stack.push(ARR_VAL_COMPLETED);
13535
- }
13536
- }
13537
-
13538
- function _isEscaped(str, pos) {
13539
- const previousChar = str[pos - 1];
13540
-
13541
- return previousChar === '\\' && !_isEscaped(str, pos - 1);
13542
- }
13543
-
13544
- /* eslint-disable max-lines */
13545
-
13546
- /**
13547
- * Takes an incomplete JSON string, and returns a hopefully valid JSON string.
13548
- * Note that this _can_ fail, so you should check the return value is valid JSON.
13549
- */
13550
- function fixJson(incompleteJson) {
13551
- const stack = evaluateJson(incompleteJson);
13552
-
13553
- return completeJson(incompleteJson, stack);
13554
- }
13555
-
13556
12588
  /** Get the size of a body. */
13557
12589
  function getBodySize(
13558
12590
  body,
@@ -13646,68 +12678,51 @@ function makeNetworkReplayBreadcrumb(
13646
12678
  return result;
13647
12679
  }
13648
12680
 
13649
- /** Build the request or response part of a replay network breadcrumb that was skipped. */
13650
- function buildSkippedNetworkRequestOrResponse(bodySize) {
13651
- return {
13652
- headers: {},
13653
- size: bodySize,
13654
- _meta: {
13655
- warnings: ['URL_SKIPPED'],
13656
- },
13657
- };
12681
+ /** Get either a JSON network body, or a text representation. */
12682
+ function getNetworkBody(bodyText) {
12683
+ if (!bodyText) {
12684
+ return;
12685
+ }
12686
+
12687
+ try {
12688
+ return JSON.parse(bodyText);
12689
+ } catch (e2) {
12690
+ // return text
12691
+ }
12692
+
12693
+ return bodyText;
13658
12694
  }
13659
12695
 
13660
12696
  /** Build the request or response part of a replay network breadcrumb. */
13661
12697
  function buildNetworkRequestOrResponse(
13662
- headers,
13663
12698
  bodySize,
13664
12699
  body,
13665
12700
  ) {
13666
- if (!bodySize && Object.keys(headers).length === 0) {
13667
- return undefined;
13668
- }
13669
-
13670
12701
  if (!bodySize) {
13671
- return {
13672
- headers,
13673
- };
12702
+ return undefined;
13674
12703
  }
13675
12704
 
13676
12705
  if (!body) {
13677
12706
  return {
13678
- headers,
13679
12707
  size: bodySize,
13680
12708
  };
13681
12709
  }
13682
12710
 
13683
12711
  const info = {
13684
- headers,
13685
12712
  size: bodySize,
13686
12713
  };
13687
12714
 
13688
- const { body: normalizedBody, warnings } = normalizeNetworkBody(body);
13689
- info.body = normalizedBody;
13690
- if (warnings.length > 0) {
12715
+ if (bodySize < NETWORK_BODY_MAX_SIZE) {
12716
+ info.body = body;
12717
+ } else {
13691
12718
  info._meta = {
13692
- warnings,
12719
+ errors: ['MAX_BODY_SIZE_EXCEEDED'],
13693
12720
  };
13694
12721
  }
13695
12722
 
13696
12723
  return info;
13697
12724
  }
13698
12725
 
13699
- /** Filter a set of headers */
13700
- function getAllowedHeaders(headers, allowedHeaders) {
13701
- return Object.keys(headers).reduce((filteredHeaders, key) => {
13702
- const normalizedKey = key.toLowerCase();
13703
- // Avoid putting empty strings into the headers
13704
- if (allowedHeaders.includes(normalizedKey) && headers[key]) {
13705
- filteredHeaders[normalizedKey] = headers[key];
13706
- }
13707
- return filteredHeaders;
13708
- }, {});
13709
- }
13710
-
13711
12726
  function _serializeFormData(formData) {
13712
12727
  // This is a bit simplified, but gives us a decent estimate
13713
12728
  // This converts e.g. { name: 'Anne Smith', age: 13 } to 'name=Anne+Smith&age=13'
@@ -13715,78 +12730,6 @@ function _serializeFormData(formData) {
13715
12730
  return new URLSearchParams(formData).toString();
13716
12731
  }
13717
12732
 
13718
- function normalizeNetworkBody(body)
13719
-
13720
- {
13721
- if (!body || typeof body !== 'string') {
13722
- return {
13723
- body,
13724
- warnings: [],
13725
- };
13726
- }
13727
-
13728
- const exceedsSizeLimit = body.length > NETWORK_BODY_MAX_SIZE;
13729
-
13730
- if (_strIsProbablyJson(body)) {
13731
- try {
13732
- const json = exceedsSizeLimit ? fixJson(body.slice(0, NETWORK_BODY_MAX_SIZE)) : body;
13733
- const normalizedBody = JSON.parse(json);
13734
- return {
13735
- body: normalizedBody,
13736
- warnings: exceedsSizeLimit ? ['JSON_TRUNCATED'] : [],
13737
- };
13738
- } catch (e3) {
13739
- return {
13740
- body: exceedsSizeLimit ? `${body.slice(0, NETWORK_BODY_MAX_SIZE)}…` : body,
13741
- warnings: exceedsSizeLimit ? ['INVALID_JSON', 'TEXT_TRUNCATED'] : ['INVALID_JSON'],
13742
- };
13743
- }
13744
- }
13745
-
13746
- return {
13747
- body: exceedsSizeLimit ? `${body.slice(0, NETWORK_BODY_MAX_SIZE)}…` : body,
13748
- warnings: exceedsSizeLimit ? ['TEXT_TRUNCATED'] : [],
13749
- };
13750
- }
13751
-
13752
- function _strIsProbablyJson(str) {
13753
- const first = str[0];
13754
- const last = str[str.length - 1];
13755
-
13756
- // Simple check: If this does not start & end with {} or [], it's not JSON
13757
- return (first === '[' && last === ']') || (first === '{' && last === '}');
13758
- }
13759
-
13760
- /** Match an URL against a list of strings/Regex. */
13761
- function urlMatches(url, urls) {
13762
- const fullUrl = getFullUrl(url);
13763
-
13764
- return stringMatchesSomePattern(fullUrl, urls);
13765
- }
13766
-
13767
- /** exported for tests */
13768
- function getFullUrl(url, baseURI = WINDOW.document.baseURI) {
13769
- // Short circuit for common cases:
13770
- if (url.startsWith('http://') || url.startsWith('https://') || url.startsWith(WINDOW.location.origin)) {
13771
- return url;
13772
- }
13773
- const fixedUrl = new URL(url, baseURI);
13774
-
13775
- // If these do not match, we are not dealing with a relative URL, so just return it
13776
- if (fixedUrl.origin !== new URL(baseURI).origin) {
13777
- return url;
13778
- }
13779
-
13780
- const fullUrl = fixedUrl.href;
13781
-
13782
- // Remove trailing slashes, if they don't match the original URL
13783
- if (!url.endsWith('/') && fullUrl.endsWith('/')) {
13784
- return fullUrl.slice(0, -1);
13785
- }
13786
-
13787
- return fullUrl;
13788
- }
13789
-
13790
12733
  /**
13791
12734
  * Capture a fetch breadcrumb to a replay.
13792
12735
  * This adds additional data (where approriate).
@@ -13794,9 +12737,7 @@ function getFullUrl(url, baseURI = WINDOW.document.baseURI) {
13794
12737
  async function captureFetchBreadcrumbToReplay(
13795
12738
  breadcrumb,
13796
12739
  hint,
13797
- options
13798
-
13799
- ,
12740
+ options,
13800
12741
  ) {
13801
12742
  try {
13802
12743
  const data = await _prepareFetchData(breadcrumb, hint, options);
@@ -13823,7 +12764,6 @@ function enrichFetchBreadcrumb(
13823
12764
 
13824
12765
  const body = _getFetchRequestArgBody(input);
13825
12766
  const reqSize = getBodySize(body, options.textEncoder);
13826
-
13827
12767
  const resSize = response ? parseContentLengthHeader(response.headers.get('content-length')) : undefined;
13828
12768
 
13829
12769
  if (reqSize !== undefined) {
@@ -13837,109 +12777,97 @@ function enrichFetchBreadcrumb(
13837
12777
  async function _prepareFetchData(
13838
12778
  breadcrumb,
13839
12779
  hint,
13840
- options
13841
-
13842
- ,
12780
+ options,
13843
12781
  ) {
13844
12782
  const { startTimestamp, endTimestamp } = hint;
13845
12783
 
13846
12784
  const {
13847
12785
  url,
13848
12786
  method,
13849
- status_code: statusCode = 0,
12787
+ status_code: statusCode,
13850
12788
  request_body_size: requestBodySize,
13851
12789
  response_body_size: responseBodySize,
13852
12790
  } = breadcrumb.data;
13853
12791
 
13854
- const captureDetails = urlMatches(url, options.networkDetailAllowUrls);
13855
-
13856
- const request = captureDetails
13857
- ? _getRequestInfo(options, hint.input, requestBodySize)
13858
- : buildSkippedNetworkRequestOrResponse(requestBodySize);
13859
- const response = await _getResponseInfo(captureDetails, options, hint.response, responseBodySize);
12792
+ const request = _getRequestInfo(options, hint.input, requestBodySize);
12793
+ const response = await _getResponseInfo(options, hint.response, responseBodySize);
13860
12794
 
13861
12795
  return {
13862
12796
  startTimestamp,
13863
12797
  endTimestamp,
13864
12798
  url,
13865
12799
  method,
13866
- statusCode,
12800
+ statusCode: statusCode || 0,
13867
12801
  request,
13868
12802
  response,
13869
12803
  };
13870
12804
  }
13871
12805
 
13872
12806
  function _getRequestInfo(
13873
- { networkCaptureBodies, networkRequestHeaders },
12807
+ { captureBodies },
13874
12808
  input,
13875
12809
  requestBodySize,
13876
12810
  ) {
13877
- const headers = getRequestHeaders(input, networkRequestHeaders);
13878
-
13879
- if (!networkCaptureBodies) {
13880
- return buildNetworkRequestOrResponse(headers, requestBodySize, undefined);
12811
+ if (!captureBodies) {
12812
+ return buildNetworkRequestOrResponse(requestBodySize, undefined);
13881
12813
  }
13882
12814
 
13883
12815
  // We only want to transmit string or string-like bodies
13884
12816
  const requestBody = _getFetchRequestArgBody(input);
13885
- const bodyStr = getBodyString(requestBody);
13886
- return buildNetworkRequestOrResponse(headers, requestBodySize, bodyStr);
12817
+ const body = getNetworkBody(getBodyString(requestBody));
12818
+ return buildNetworkRequestOrResponse(requestBodySize, body);
13887
12819
  }
13888
12820
 
13889
12821
  async function _getResponseInfo(
13890
- captureDetails,
13891
- {
13892
- networkCaptureBodies,
13893
- textEncoder,
13894
- networkResponseHeaders,
13895
- }
13896
-
13897
- ,
12822
+ { captureBodies, textEncoder },
13898
12823
  response,
13899
12824
  responseBodySize,
13900
12825
  ) {
13901
- if (!captureDetails && responseBodySize !== undefined) {
13902
- return buildSkippedNetworkRequestOrResponse(responseBodySize);
13903
- }
13904
-
13905
- const headers = getAllHeaders(response.headers, networkResponseHeaders);
13906
-
13907
- if (!networkCaptureBodies && responseBodySize !== undefined) {
13908
- return buildNetworkRequestOrResponse(headers, responseBodySize, undefined);
12826
+ if (!captureBodies && responseBodySize !== undefined) {
12827
+ return buildNetworkRequestOrResponse(responseBodySize, undefined);
13909
12828
  }
13910
12829
 
13911
12830
  // Only clone the response if we need to
13912
12831
  try {
13913
12832
  // We have to clone this, as the body can only be read once
13914
12833
  const res = response.clone();
13915
- const bodyText = await _parseFetchBody(res);
12834
+ const { body, bodyText } = await _parseFetchBody(res);
13916
12835
 
13917
12836
  const size =
13918
12837
  bodyText && bodyText.length && responseBodySize === undefined
13919
12838
  ? getBodySize(bodyText, textEncoder)
13920
12839
  : responseBodySize;
13921
12840
 
13922
- if (!captureDetails) {
13923
- return buildSkippedNetworkRequestOrResponse(size);
12841
+ if (captureBodies) {
12842
+ return buildNetworkRequestOrResponse(size, body);
13924
12843
  }
13925
12844
 
13926
- if (networkCaptureBodies) {
13927
- return buildNetworkRequestOrResponse(headers, size, bodyText);
13928
- }
13929
-
13930
- return buildNetworkRequestOrResponse(headers, size, undefined);
12845
+ return buildNetworkRequestOrResponse(size, undefined);
13931
12846
  } catch (e) {
13932
12847
  // fallback
13933
- return buildNetworkRequestOrResponse(headers, responseBodySize, undefined);
12848
+ return buildNetworkRequestOrResponse(responseBodySize, undefined);
13934
12849
  }
13935
12850
  }
13936
12851
 
13937
- async function _parseFetchBody(response) {
12852
+ async function _parseFetchBody(
12853
+ response,
12854
+ ) {
12855
+ let bodyText;
12856
+
13938
12857
  try {
13939
- return await response.text();
12858
+ bodyText = await response.text();
13940
12859
  } catch (e2) {
13941
- return undefined;
12860
+ return {};
13942
12861
  }
12862
+
12863
+ try {
12864
+ const body = JSON.parse(bodyText);
12865
+ return { body, bodyText };
12866
+ } catch (e3) {
12867
+ // just send bodyText
12868
+ }
12869
+
12870
+ return { bodyText, body: bodyText };
13943
12871
  }
13944
12872
 
13945
12873
  function _getFetchRequestArgBody(fetchArgs = []) {
@@ -13951,56 +12879,6 @@ function _getFetchRequestArgBody(fetchArgs = []) {
13951
12879
  return (fetchArgs[1] ).body;
13952
12880
  }
13953
12881
 
13954
- function getAllHeaders(headers, allowedHeaders) {
13955
- const allHeaders = {};
13956
-
13957
- allowedHeaders.forEach(header => {
13958
- if (headers.get(header)) {
13959
- allHeaders[header] = headers.get(header) ;
13960
- }
13961
- });
13962
-
13963
- return allHeaders;
13964
- }
13965
-
13966
- function getRequestHeaders(fetchArgs, allowedHeaders) {
13967
- if (fetchArgs.length === 1 && typeof fetchArgs[0] !== 'string') {
13968
- return getHeadersFromOptions(fetchArgs[0] , allowedHeaders);
13969
- }
13970
-
13971
- if (fetchArgs.length === 2) {
13972
- return getHeadersFromOptions(fetchArgs[1] , allowedHeaders);
13973
- }
13974
-
13975
- return {};
13976
- }
13977
-
13978
- function getHeadersFromOptions(
13979
- input,
13980
- allowedHeaders,
13981
- ) {
13982
- if (!input) {
13983
- return {};
13984
- }
13985
-
13986
- const headers = input.headers;
13987
-
13988
- if (!headers) {
13989
- return {};
13990
- }
13991
-
13992
- if (headers instanceof Headers) {
13993
- return getAllHeaders(headers, allowedHeaders);
13994
- }
13995
-
13996
- // We do not support this, as it is not really documented (anymore?)
13997
- if (Array.isArray(headers)) {
13998
- return {};
13999
- }
14000
-
14001
- return getAllowedHeaders(headers, allowedHeaders);
14002
- }
14003
-
14004
12882
  /**
14005
12883
  * Capture an XHR breadcrumb to a replay.
14006
12884
  * This adds additional data (where approriate).
@@ -14051,12 +12929,12 @@ function _prepareXhrData(
14051
12929
  hint,
14052
12930
  options,
14053
12931
  ) {
14054
- const { startTimestamp, endTimestamp, input, xhr } = hint;
12932
+ const { startTimestamp, endTimestamp, input } = hint;
14055
12933
 
14056
12934
  const {
14057
12935
  url,
14058
12936
  method,
14059
- status_code: statusCode = 0,
12937
+ status_code: statusCode,
14060
12938
  request_body_size: requestBodySize,
14061
12939
  response_body_size: responseBodySize,
14062
12940
  } = breadcrumb.data;
@@ -14065,35 +12943,13 @@ function _prepareXhrData(
14065
12943
  return null;
14066
12944
  }
14067
12945
 
14068
- if (!urlMatches(url, options.networkDetailAllowUrls)) {
14069
- const request = buildSkippedNetworkRequestOrResponse(requestBodySize);
14070
- const response = buildSkippedNetworkRequestOrResponse(responseBodySize);
14071
- return {
14072
- startTimestamp,
14073
- endTimestamp,
14074
- url,
14075
- method,
14076
- statusCode,
14077
- request,
14078
- response,
14079
- };
14080
- }
14081
-
14082
- const xhrInfo = xhr[SENTRY_XHR_DATA_KEY];
14083
- const networkRequestHeaders = xhrInfo
14084
- ? getAllowedHeaders(xhrInfo.request_headers, options.networkRequestHeaders)
14085
- : {};
14086
- const networkResponseHeaders = getAllowedHeaders(getResponseHeaders(xhr), options.networkResponseHeaders);
14087
-
14088
12946
  const request = buildNetworkRequestOrResponse(
14089
- networkRequestHeaders,
14090
12947
  requestBodySize,
14091
- options.networkCaptureBodies ? getBodyString(input) : undefined,
12948
+ options.captureBodies ? getNetworkBody(getBodyString(input)) : undefined,
14092
12949
  );
14093
12950
  const response = buildNetworkRequestOrResponse(
14094
- networkResponseHeaders,
14095
12951
  responseBodySize,
14096
- options.networkCaptureBodies ? hint.xhr.responseText : undefined,
12952
+ options.captureBodies ? getNetworkBody(hint.xhr.responseText) : undefined,
14097
12953
  );
14098
12954
 
14099
12955
  return {
@@ -14101,26 +12957,12 @@ function _prepareXhrData(
14101
12957
  endTimestamp,
14102
12958
  url,
14103
12959
  method,
14104
- statusCode,
12960
+ statusCode: statusCode || 0,
14105
12961
  request,
14106
12962
  response,
14107
12963
  };
14108
12964
  }
14109
12965
 
14110
- function getResponseHeaders(xhr) {
14111
- const headers = xhr.getAllResponseHeaders();
14112
-
14113
- if (!headers) {
14114
- return {};
14115
- }
14116
-
14117
- return headers.split('\r\n').reduce((acc, line) => {
14118
- const [key, value] = line.split(': ');
14119
- acc[key.toLowerCase()] = value;
14120
- return acc;
14121
- }, {});
14122
- }
14123
-
14124
12966
  /**
14125
12967
  * This method does two things:
14126
12968
  * - It enriches the regular XHR/fetch breadcrumbs with request/response size data
@@ -14133,16 +12975,10 @@ function handleNetworkBreadcrumbs(replay) {
14133
12975
  try {
14134
12976
  const textEncoder = new TextEncoder();
14135
12977
 
14136
- const { networkDetailAllowUrls, networkCaptureBodies, networkRequestHeaders, networkResponseHeaders } =
14137
- replay.getOptions();
14138
-
14139
12978
  const options = {
14140
12979
  replay,
14141
12980
  textEncoder,
14142
- networkDetailAllowUrls,
14143
- networkCaptureBodies,
14144
- networkRequestHeaders,
14145
- networkResponseHeaders,
12981
+ captureBodies: replay.getOptions()._experiments.captureNetworkBodies || false,
14146
12982
  };
14147
12983
 
14148
12984
  if (client && client.on) {
@@ -14250,66 +13086,9 @@ function handleScope(scope) {
14250
13086
  return null;
14251
13087
  }
14252
13088
 
14253
- if (newBreadcrumb.category === 'console') {
14254
- return normalizeConsoleBreadcrumb(newBreadcrumb);
14255
- }
14256
-
14257
13089
  return createBreadcrumb(newBreadcrumb);
14258
13090
  }
14259
13091
 
14260
- /** exported for tests only */
14261
- function normalizeConsoleBreadcrumb(breadcrumb) {
14262
- const args = breadcrumb.data && breadcrumb.data.arguments;
14263
-
14264
- if (!Array.isArray(args) || args.length === 0) {
14265
- return createBreadcrumb(breadcrumb);
14266
- }
14267
-
14268
- let isTruncated = false;
14269
-
14270
- // Avoid giant args captures
14271
- const normalizedArgs = args.map(arg => {
14272
- if (!arg) {
14273
- return arg;
14274
- }
14275
- if (typeof arg === 'string') {
14276
- if (arg.length > CONSOLE_ARG_MAX_SIZE) {
14277
- isTruncated = true;
14278
- return `${arg.slice(0, CONSOLE_ARG_MAX_SIZE)}…`;
14279
- }
14280
-
14281
- return arg;
14282
- }
14283
- if (typeof arg === 'object') {
14284
- try {
14285
- const normalizedArg = normalize(arg, 7);
14286
- const stringified = JSON.stringify(normalizedArg);
14287
- if (stringified.length > CONSOLE_ARG_MAX_SIZE) {
14288
- const fixedJson = fixJson(stringified.slice(0, CONSOLE_ARG_MAX_SIZE));
14289
- const json = JSON.parse(fixedJson);
14290
- // We only set this after JSON.parse() was successfull, so we know we didn't run into `catch`
14291
- isTruncated = true;
14292
- return json;
14293
- }
14294
- return normalizedArg;
14295
- } catch (e) {
14296
- // fall back to default
14297
- }
14298
- }
14299
-
14300
- return arg;
14301
- });
14302
-
14303
- return createBreadcrumb({
14304
- ...breadcrumb,
14305
- data: {
14306
- ...breadcrumb.data,
14307
- arguments: normalizedArgs,
14308
- ...(isTruncated ? { _meta: { warnings: ['CONSOLE_ARG_TRUNCATED'] } } : {}),
14309
- },
14310
- });
14311
- }
14312
-
14313
13092
  /**
14314
13093
  * Add global listeners that cannot be removed.
14315
13094
  */
@@ -14334,8 +13113,7 @@ function addGlobalListeners(replay) {
14334
13113
  client.on('afterSendEvent', handleAfterSendEvent(replay));
14335
13114
  client.on('createDsc', (dsc) => {
14336
13115
  const replayId = replay.getSessionId();
14337
- // We do not want to set the DSC when in buffer mode, as that means the replay has not been sent (yet)
14338
- if (replayId && replay.isEnabled() && replay.recordingMode === 'session') {
13116
+ if (replayId) {
14339
13117
  dsc.replay_id = replayId;
14340
13118
  }
14341
13119
  });
@@ -14615,23 +13393,6 @@ function debounce(func, wait, options) {
14615
13393
  return debounced;
14616
13394
  }
14617
13395
 
14618
- /* eslint-disable @typescript-eslint/naming-convention */
14619
-
14620
- var EventType; (function (EventType) {
14621
- const DomContentLoaded = 0; EventType[EventType["DomContentLoaded"] = DomContentLoaded] = "DomContentLoaded";
14622
- const Load = 1; EventType[EventType["Load"] = Load] = "Load";
14623
- const FullSnapshot = 2; EventType[EventType["FullSnapshot"] = FullSnapshot] = "FullSnapshot";
14624
- const IncrementalSnapshot = 3; EventType[EventType["IncrementalSnapshot"] = IncrementalSnapshot] = "IncrementalSnapshot";
14625
- const Meta = 4; EventType[EventType["Meta"] = Meta] = "Meta";
14626
- const Custom = 5; EventType[EventType["Custom"] = Custom] = "Custom";
14627
- const Plugin = 6; EventType[EventType["Plugin"] = Plugin] = "Plugin";
14628
- })(EventType || (EventType = {}));
14629
-
14630
- /**
14631
- * This is a partial copy of rrweb's eventWithTime type which only contains the properties
14632
- * we specifcally need in the SDK.
14633
- */
14634
-
14635
13396
  /**
14636
13397
  * Handler for recording events.
14637
13398
  *
@@ -14660,7 +13421,7 @@ function getHandleRecordingEmit(replay) {
14660
13421
  // when an error occurs. Clear any state that happens before this current
14661
13422
  // checkout. This needs to happen before `addEvent()` which updates state
14662
13423
  // dependent on this reset.
14663
- if (replay.recordingMode === 'buffer' && isCheckout) {
13424
+ if (replay.recordingMode === 'error' && isCheckout) {
14664
13425
  replay.setInitialState();
14665
13426
  }
14666
13427
 
@@ -14674,14 +13435,6 @@ function getHandleRecordingEmit(replay) {
14674
13435
  return false;
14675
13436
  }
14676
13437
 
14677
- // Additionally, create a meta event that will capture certain SDK settings.
14678
- // In order to handle buffer mode, this needs to either be done when we
14679
- // receive checkout events or at flush time.
14680
- //
14681
- // `isCheckout` is always true, but want to be explicit that it should
14682
- // only be added for checkouts
14683
- void addSettingsEvent(replay, isCheckout);
14684
-
14685
13438
  // If there is a previousSessionId after a full snapshot occurs, then
14686
13439
  // the replay session was started due to session expiration. The new session
14687
13440
  // is started before triggering a new checkout and contains the id
@@ -14692,10 +13445,10 @@ function getHandleRecordingEmit(replay) {
14692
13445
  return true;
14693
13446
  }
14694
13447
 
14695
- // When in buffer mode, make sure we adjust the session started date to the current earliest event of the buffer
14696
- // this should usually be the timestamp of the checkout event, but to be safe...
14697
- if (replay.recordingMode === 'buffer' && replay.session && replay.eventBuffer) {
14698
- const earliestEvent = replay.eventBuffer.getEarliestTimestamp();
13448
+ // See note above re: session start needs to reflect the most recent
13449
+ // checkout.
13450
+ if (replay.recordingMode === 'error' && replay.session) {
13451
+ const { earliestEvent } = replay.getContext();
14699
13452
  if (earliestEvent) {
14700
13453
  replay.session.started = earliestEvent;
14701
13454
 
@@ -14719,46 +13472,6 @@ function getHandleRecordingEmit(replay) {
14719
13472
  };
14720
13473
  }
14721
13474
 
14722
- /**
14723
- * Exported for tests
14724
- */
14725
- function createOptionsEvent(replay) {
14726
- const options = replay.getOptions();
14727
- return {
14728
- type: EventType.Custom,
14729
- timestamp: Date.now(),
14730
- data: {
14731
- tag: 'options',
14732
- payload: {
14733
- sessionSampleRate: options.sessionSampleRate,
14734
- errorSampleRate: options.errorSampleRate,
14735
- useCompressionOption: options.useCompression,
14736
- blockAllMedia: options.blockAllMedia,
14737
- maskAllText: options.maskAllText,
14738
- maskAllInputs: options.maskAllInputs,
14739
- useCompression: replay.eventBuffer ? replay.eventBuffer.type === 'worker' : false,
14740
- networkDetailHasUrls: options.networkDetailAllowUrls.length > 0,
14741
- networkCaptureBodies: options.networkCaptureBodies,
14742
- networkRequestHasHeaders: options.networkRequestHeaders.length > 0,
14743
- networkResponseHasHeaders: options.networkResponseHeaders.length > 0,
14744
- },
14745
- },
14746
- };
14747
- }
14748
-
14749
- /**
14750
- * Add a "meta" event that contains a simplified view on current configuration
14751
- * options. This should only be included on the first segment of a recording.
14752
- */
14753
- function addSettingsEvent(replay, isCheckout) {
14754
- // Only need to add this event when sending the first segment
14755
- if (!isCheckout || !replay.session || replay.session.segmentId !== 0) {
14756
- return Promise.resolve(null);
14757
- }
14758
-
14759
- return addEvent(replay, createOptionsEvent(replay), false);
14760
- }
14761
-
14762
13475
  /**
14763
13476
  * Create a replay envelope ready to be sent.
14764
13477
  * This includes both the replay event, as well as the recording data.
@@ -14870,9 +13583,11 @@ async function sendReplayRequest({
14870
13583
  recordingData,
14871
13584
  replayId,
14872
13585
  segmentId: segment_id,
13586
+ includeReplayStartTimestamp,
14873
13587
  eventContext,
14874
13588
  timestamp,
14875
13589
  session,
13590
+ options,
14876
13591
  }) {
14877
13592
  const preparedRecordingData = prepareRecordingData({
14878
13593
  recordingData,
@@ -14894,8 +13609,9 @@ async function sendReplayRequest({
14894
13609
  }
14895
13610
 
14896
13611
  const baseEvent = {
13612
+ // @ts-ignore private api
14897
13613
  type: REPLAY_EVENT_NAME,
14898
- replay_start_timestamp: initialTimestamp / 1000,
13614
+ ...(includeReplayStartTimestamp ? { replay_start_timestamp: initialTimestamp / 1000 } : {}),
14899
13615
  timestamp: timestamp / 1000,
14900
13616
  error_ids: errorIds,
14901
13617
  trace_ids: traceIds,
@@ -14914,6 +13630,15 @@ async function sendReplayRequest({
14914
13630
  return;
14915
13631
  }
14916
13632
 
13633
+ replayEvent.contexts = {
13634
+ ...replayEvent.contexts,
13635
+ replay: {
13636
+ ...(replayEvent.contexts && replayEvent.contexts.replay),
13637
+ session_sample_rate: options.sessionSampleRate,
13638
+ error_sample_rate: options.errorSampleRate,
13639
+ },
13640
+ };
13641
+
14917
13642
  /*
14918
13643
  For reference, the fully built event looks something like this:
14919
13644
  {
@@ -14944,6 +13669,10 @@ async function sendReplayRequest({
14944
13669
  },
14945
13670
  "sdkProcessingMetadata": {},
14946
13671
  "contexts": {
13672
+ "replay": {
13673
+ "session_sample_rate": 1,
13674
+ "error_sample_rate": 0,
13675
+ },
14947
13676
  },
14948
13677
  }
14949
13678
  */
@@ -15069,11 +13798,9 @@ class ReplayContainer {
15069
13798
  __init2() {this.performanceEvents = [];}
15070
13799
 
15071
13800
  /**
15072
- * Recording can happen in one of three modes:
15073
- * - session: Record the whole session, sending it continuously
15074
- * - buffer: Always keep the last 60s of recording, requires:
15075
- * - having replaysOnErrorSampleRate > 0 to capture replay when an error occurs
15076
- * - or calling `flush()` to send the replay
13801
+ * Recording can happen in one of two modes:
13802
+ * * session: Record the whole session, sending it continuously
13803
+ * * error: Always keep the last 60s of recording, and when an error occurs, send it immediately
15077
13804
  */
15078
13805
  __init3() {this.recordingMode = 'session';}
15079
13806
 
@@ -15082,8 +13809,7 @@ class ReplayContainer {
15082
13809
  * @hidden
15083
13810
  */
15084
13811
  __init4() {this.timeouts = {
15085
- sessionIdlePause: SESSION_IDLE_PAUSE_DURATION,
15086
- sessionIdleExpire: SESSION_IDLE_EXPIRE_DURATION,
13812
+ sessionIdle: SESSION_IDLE_DURATION,
15087
13813
  maxSessionLife: MAX_SESSION_LIFE,
15088
13814
  }; }
15089
13815
 
@@ -15127,6 +13853,7 @@ class ReplayContainer {
15127
13853
  errorIds: new Set(),
15128
13854
  traceIds: new Set(),
15129
13855
  urls: [],
13856
+ earliestEvent: null,
15130
13857
  initialTimestamp: Date.now(),
15131
13858
  initialUrl: '',
15132
13859
  };}
@@ -15136,7 +13863,7 @@ class ReplayContainer {
15136
13863
  recordingOptions,
15137
13864
  }
15138
13865
 
15139
- ) {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);
13866
+ ) {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);
15140
13867
  this._recordingOptions = recordingOptions;
15141
13868
  this._options = options;
15142
13869
 
@@ -15166,102 +13893,49 @@ class ReplayContainer {
15166
13893
  }
15167
13894
 
15168
13895
  /**
15169
- * Initializes the plugin based on sampling configuration. Should not be
15170
- * called outside of constructor.
13896
+ * Initializes the plugin.
13897
+ *
13898
+ * Creates or loads a session, attaches listeners to varying events (DOM,
13899
+ * _performanceObserver, Recording, Sentry SDK, etc)
15171
13900
  */
15172
- initializeSampling() {
15173
- const { errorSampleRate, sessionSampleRate } = this._options;
15174
-
15175
- // If neither sample rate is > 0, then do nothing - user will need to call one of
15176
- // `start()` or `startBuffering` themselves.
15177
- if (errorSampleRate <= 0 && sessionSampleRate <= 0) {
15178
- return;
15179
- }
15180
-
15181
- // Otherwise if there is _any_ sample rate set, try to load an existing
15182
- // session, or create a new one.
15183
- const isSessionSampled = this._loadAndCheckSession();
13901
+ start() {
13902
+ this.setInitialState();
15184
13903
 
15185
- if (!isSessionSampled) {
15186
- // This should only occur if `errorSampleRate` is 0 and was unsampled for
15187
- // session-based replay. In this case there is nothing to do.
13904
+ if (!this._loadAndCheckSession()) {
15188
13905
  return;
15189
13906
  }
15190
13907
 
13908
+ // If there is no session, then something bad has happened - can't continue
15191
13909
  if (!this.session) {
15192
- // This should not happen, something wrong has occurred
15193
- this._handleException(new Error('Unable to initialize and create session'));
13910
+ this._handleException(new Error('No session found'));
15194
13911
  return;
15195
13912
  }
15196
13913
 
15197
- if (this.session.sampled && this.session.sampled !== 'session') {
15198
- // If not sampled as session-based, then recording mode will be `buffer`
15199
- // Note that we don't explicitly check if `sampled === 'buffer'` because we
15200
- // could have sessions from Session storage that are still `error` from
15201
- // prior SDK version.
15202
- this.recordingMode = 'buffer';
15203
- }
15204
-
15205
- this._initializeRecording();
15206
- }
15207
-
15208
- /**
15209
- * Start a replay regardless of sampling rate. Calling this will always
15210
- * create a new session. Will throw an error if replay is already in progress.
15211
- *
15212
- * Creates or loads a session, attaches listeners to varying events (DOM,
15213
- * _performanceObserver, Recording, Sentry SDK, etc)
15214
- */
15215
- start() {
15216
- if (this._isEnabled && this.recordingMode === 'session') {
15217
- throw new Error('Replay recording is already in progress');
13914
+ if (!this.session.sampled) {
13915
+ // If session was not sampled, then we do not initialize the integration at all.
13916
+ return;
15218
13917
  }
15219
13918
 
15220
- if (this._isEnabled && this.recordingMode === 'buffer') {
15221
- throw new Error('Replay buffering is in progress, call `flush()` to save the replay');
13919
+ // If session is sampled for errors, then we need to set the recordingMode
13920
+ // to 'error', which will configure recording with different options.
13921
+ if (this.session.sampled === 'error') {
13922
+ this.recordingMode = 'error';
15222
13923
  }
15223
13924
 
15224
- const previousSessionId = this.session && this.session.id;
13925
+ // setup() is generally called on page load or manually - in both cases we
13926
+ // should treat it as an activity
13927
+ this._updateSessionActivity();
15225
13928
 
15226
- const { session } = getSession({
15227
- timeouts: this.timeouts,
15228
- stickySession: Boolean(this._options.stickySession),
15229
- currentSession: this.session,
15230
- // This is intentional: create a new session-based replay when calling `start()`
15231
- sessionSampleRate: 1,
15232
- allowBuffering: false,
13929
+ this.eventBuffer = createEventBuffer({
13930
+ useCompression: this._options.useCompression,
15233
13931
  });
15234
13932
 
15235
- session.previousSessionId = previousSessionId;
15236
- this.session = session;
15237
-
15238
- this._initializeRecording();
15239
- }
15240
-
15241
- /**
15242
- * Start replay buffering. Buffers until `flush()` is called or, if
15243
- * `replaysOnErrorSampleRate` > 0, an error occurs.
15244
- */
15245
- startBuffering() {
15246
- if (this._isEnabled) {
15247
- throw new Error('Replay recording is already in progress');
15248
- }
15249
-
15250
- const previousSessionId = this.session && this.session.id;
15251
-
15252
- const { session } = getSession({
15253
- timeouts: this.timeouts,
15254
- stickySession: Boolean(this._options.stickySession),
15255
- currentSession: this.session,
15256
- sessionSampleRate: 0,
15257
- allowBuffering: true,
15258
- });
13933
+ this._addListeners();
15259
13934
 
15260
- session.previousSessionId = previousSessionId;
15261
- this.session = session;
13935
+ // Need to set as enabled before we start recording, as `record()` can trigger a flush with a new checkout
13936
+ this._isEnabled = true;
15262
13937
 
15263
- this.recordingMode = 'buffer';
15264
- this._initializeRecording();
13938
+ this.startRecording();
15265
13939
  }
15266
13940
 
15267
13941
  /**
@@ -15276,7 +13950,7 @@ class ReplayContainer {
15276
13950
  // When running in error sampling mode, we need to overwrite `checkoutEveryNms`
15277
13951
  // Without this, it would record forever, until an error happens, which we don't want
15278
13952
  // instead, we'll always keep the last 60 seconds of replay before an error happened
15279
- ...(this.recordingMode === 'buffer' && { checkoutEveryNms: BUFFER_CHECKOUT_TIME }),
13953
+ ...(this.recordingMode === 'error' && { checkoutEveryNms: ERROR_CHECKOUT_TIME }),
15280
13954
  emit: getHandleRecordingEmit(this),
15281
13955
  onMutation: this._onMutationHandler,
15282
13956
  });
@@ -15287,18 +13961,17 @@ class ReplayContainer {
15287
13961
 
15288
13962
  /**
15289
13963
  * Stops the recording, if it was running.
15290
- *
15291
- * Returns true if it was previously stopped, or is now stopped,
15292
- * otherwise false.
13964
+ * Returns true if it was stopped, else false.
15293
13965
  */
15294
13966
  stopRecording() {
15295
13967
  try {
15296
13968
  if (this._stopRecording) {
15297
13969
  this._stopRecording();
15298
13970
  this._stopRecording = undefined;
13971
+ return true;
15299
13972
  }
15300
13973
 
15301
- return true;
13974
+ return false;
15302
13975
  } catch (err) {
15303
13976
  this._handleException(err);
15304
13977
  return false;
@@ -15309,7 +13982,7 @@ class ReplayContainer {
15309
13982
  * Currently, this needs to be manually called (e.g. for tests). Sentry SDK
15310
13983
  * does not support a teardown
15311
13984
  */
15312
- async stop(reason) {
13985
+ stop(reason) {
15313
13986
  if (!this._isEnabled) {
15314
13987
  return;
15315
13988
  }
@@ -15325,24 +13998,12 @@ class ReplayContainer {
15325
13998
  log(msg);
15326
13999
  }
15327
14000
 
15328
- // We can't move `_isEnabled` after awaiting a flush, otherwise we can
15329
- // enter into an infinite loop when `stop()` is called while flushing.
15330
14001
  this._isEnabled = false;
15331
14002
  this._removeListeners();
15332
14003
  this.stopRecording();
15333
-
15334
- this._debouncedFlush.cancel();
15335
- // See comment above re: `_isEnabled`, we "force" a flush, ignoring the
15336
- // `_isEnabled` state of the plugin since it was disabled above.
15337
- await this._flush({ force: true });
15338
-
15339
- // After flush, destroy event buffer
15340
14004
  this.eventBuffer && this.eventBuffer.destroy();
15341
14005
  this.eventBuffer = null;
15342
-
15343
- // Clear session from session storage, note this means if a new session
15344
- // is started after, it will not have `previousSessionId`
15345
- clearSession(this);
14006
+ this._debouncedFlush.cancel();
15346
14007
  } catch (err) {
15347
14008
  this._handleException(err);
15348
14009
  }
@@ -15373,45 +14034,6 @@ class ReplayContainer {
15373
14034
  this.startRecording();
15374
14035
  }
15375
14036
 
15376
- /**
15377
- * If not in "session" recording mode, flush event buffer which will create a new replay.
15378
- * Unless `continueRecording` is false, the replay will continue to record and
15379
- * behave as a "session"-based replay.
15380
- *
15381
- * Otherwise, queue up a flush.
15382
- */
15383
- async sendBufferedReplayOrFlush({ continueRecording = true } = {}) {
15384
- if (this.recordingMode === 'session') {
15385
- return this.flushImmediate();
15386
- }
15387
-
15388
- // Allow flush to complete before resuming as a session recording, otherwise
15389
- // the checkout from `startRecording` may be included in the payload.
15390
- // Prefer to keep the error replay as a separate (and smaller) segment
15391
- // than the session replay.
15392
- await this.flushImmediate();
15393
-
15394
- const hasStoppedRecording = this.stopRecording();
15395
-
15396
- if (!continueRecording || !hasStoppedRecording) {
15397
- return;
15398
- }
15399
-
15400
- // Re-start recording, but in "session" recording mode
15401
-
15402
- // Reset all "capture on error" configuration before
15403
- // starting a new recording
15404
- this.recordingMode = 'session';
15405
-
15406
- // Once this session ends, we do not want to refresh it
15407
- if (this.session) {
15408
- this.session.shouldRefresh = false;
15409
- this._maybeSaveSession();
15410
- }
15411
-
15412
- this.startRecording();
15413
- }
15414
-
15415
14037
  /**
15416
14038
  * We want to batch uploads of replay events. Save events only if
15417
14039
  * `<flushMinDelay>` milliseconds have elapsed since the last event
@@ -15421,12 +14043,12 @@ class ReplayContainer {
15421
14043
  * processing and hand back control to caller.
15422
14044
  */
15423
14045
  addUpdate(cb) {
15424
- // We need to always run `cb` (e.g. in the case of `this.recordingMode == 'buffer'`)
14046
+ // We need to always run `cb` (e.g. in the case of `this.recordingMode == 'error'`)
15425
14047
  const cbResult = cb();
15426
14048
 
15427
14049
  // If this option is turned on then we will only want to call `flush`
15428
14050
  // explicitly
15429
- if (this.recordingMode === 'buffer') {
14051
+ if (this.recordingMode === 'error') {
15430
14052
  return;
15431
14053
  }
15432
14054
 
@@ -15498,12 +14120,12 @@ class ReplayContainer {
15498
14120
  const oldSessionId = this.getSessionId();
15499
14121
 
15500
14122
  // Prevent starting a new session if the last user activity is older than
15501
- // SESSION_IDLE_PAUSE_DURATION. Otherwise non-user activity can trigger a new
14123
+ // SESSION_IDLE_DURATION. Otherwise non-user activity can trigger a new
15502
14124
  // session+recording. This creates noisy replays that do not have much
15503
14125
  // content in them.
15504
14126
  if (
15505
14127
  this._lastActivity &&
15506
- isExpired(this._lastActivity, this.timeouts.sessionIdlePause) &&
14128
+ isExpired(this._lastActivity, this.timeouts.sessionIdle) &&
15507
14129
  this.session &&
15508
14130
  this.session.sampled === 'session'
15509
14131
  ) {
@@ -15553,30 +14175,6 @@ class ReplayContainer {
15553
14175
  this._context.urls.push(url);
15554
14176
  }
15555
14177
 
15556
- /**
15557
- * Initialize and start all listeners to varying events (DOM,
15558
- * Performance Observer, Recording, Sentry SDK, etc)
15559
- */
15560
- _initializeRecording() {
15561
- this.setInitialState();
15562
-
15563
- // this method is generally called on page load or manually - in both cases
15564
- // we should treat it as an activity
15565
- this._updateSessionActivity();
15566
-
15567
- this.eventBuffer = createEventBuffer({
15568
- useCompression: this._options.useCompression,
15569
- });
15570
-
15571
- this._removeListeners();
15572
- this._addListeners();
15573
-
15574
- // Need to set as enabled before we start recording, as `record()` can trigger a flush with a new checkout
15575
- this._isEnabled = true;
15576
-
15577
- this.startRecording();
15578
- }
15579
-
15580
14178
  /** A wrapper to conditionally capture exceptions. */
15581
14179
  _handleException(error) {
15582
14180
  (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.error('[Replay]', error);
@@ -15596,7 +14194,7 @@ class ReplayContainer {
15596
14194
  stickySession: Boolean(this._options.stickySession),
15597
14195
  currentSession: this.session,
15598
14196
  sessionSampleRate: this._options.sessionSampleRate,
15599
- allowBuffering: this._options.errorSampleRate > 0,
14197
+ errorSampleRate: this._options.errorSampleRate,
15600
14198
  });
15601
14199
 
15602
14200
  // If session was newly created (i.e. was not loaded from storage), then
@@ -15613,7 +14211,7 @@ class ReplayContainer {
15613
14211
  this.session = session;
15614
14212
 
15615
14213
  if (!this.session.sampled) {
15616
- void this.stop('session unsampled');
14214
+ this.stop('session unsampled');
15617
14215
  return false;
15618
14216
  }
15619
14217
 
@@ -15628,7 +14226,6 @@ class ReplayContainer {
15628
14226
  WINDOW.document.addEventListener('visibilitychange', this._handleVisibilityChange);
15629
14227
  WINDOW.addEventListener('blur', this._handleWindowBlur);
15630
14228
  WINDOW.addEventListener('focus', this._handleWindowFocus);
15631
- WINDOW.addEventListener('keydown', this._handleKeyboardEvent);
15632
14229
 
15633
14230
  // There is no way to remove these listeners, so ensure they are only added once
15634
14231
  if (!this._hasInitializedCoreListeners) {
@@ -15657,7 +14254,6 @@ class ReplayContainer {
15657
14254
 
15658
14255
  WINDOW.removeEventListener('blur', this._handleWindowBlur);
15659
14256
  WINDOW.removeEventListener('focus', this._handleWindowFocus);
15660
- WINDOW.removeEventListener('keydown', this._handleKeyboardEvent);
15661
14257
 
15662
14258
  if (this._performanceObserver) {
15663
14259
  this._performanceObserver.disconnect();
@@ -15708,11 +14304,6 @@ class ReplayContainer {
15708
14304
  this._doChangeToForegroundTasks(breadcrumb);
15709
14305
  };}
15710
14306
 
15711
- /** Ensure page remains active when a key is pressed. */
15712
- __init16() {this._handleKeyboardEvent = (event) => {
15713
- handleKeyboardEvent(this, event);
15714
- };}
15715
-
15716
14307
  /**
15717
14308
  * Tasks to run when we consider a page to be hidden (via blurring and/or visibility)
15718
14309
  */
@@ -15744,7 +14335,7 @@ class ReplayContainer {
15744
14335
  const isSessionActive = this.checkAndHandleExpiredSession();
15745
14336
 
15746
14337
  if (!isSessionActive) {
15747
- // If the user has come back to the page within SESSION_IDLE_PAUSE_DURATION
14338
+ // If the user has come back to the page within SESSION_IDLE_DURATION
15748
14339
  // ms, we will re-use the existing session, otherwise create a new
15749
14340
  // session
15750
14341
  (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.log('[Replay] Document has become active, but session has expired');
@@ -15792,7 +14383,7 @@ class ReplayContainer {
15792
14383
  _createCustomBreadcrumb(breadcrumb) {
15793
14384
  this.addUpdate(() => {
15794
14385
  void addEvent(this, {
15795
- type: EventType$1.Custom,
14386
+ type: EventType.Custom,
15796
14387
  timestamp: breadcrumb.timestamp || 0,
15797
14388
  data: {
15798
14389
  tag: 'breadcrumb',
@@ -15818,7 +14409,7 @@ class ReplayContainer {
15818
14409
  * Only flush if `this.recordingMode === 'session'`
15819
14410
  */
15820
14411
  _conditionalFlush() {
15821
- if (this.recordingMode === 'buffer') {
14412
+ if (this.recordingMode === 'error') {
15822
14413
  return;
15823
14414
  }
15824
14415
 
@@ -15833,35 +14424,22 @@ class ReplayContainer {
15833
14424
  this._context.errorIds.clear();
15834
14425
  this._context.traceIds.clear();
15835
14426
  this._context.urls = [];
15836
- }
15837
-
15838
- /** Update the initial timestamp based on the buffer content. */
15839
- _updateInitialTimestampFromEventBuffer() {
15840
- const { session, eventBuffer } = this;
15841
- if (!session || !eventBuffer) {
15842
- return;
15843
- }
15844
-
15845
- // we only ever update this on the initial segment
15846
- if (session.segmentId) {
15847
- return;
15848
- }
15849
-
15850
- const earliestEvent = eventBuffer.getEarliestTimestamp();
15851
- if (earliestEvent && earliestEvent < this._context.initialTimestamp) {
15852
- this._context.initialTimestamp = earliestEvent;
15853
- }
14427
+ this._context.earliestEvent = null;
15854
14428
  }
15855
14429
 
15856
14430
  /**
15857
14431
  * Return and clear _context
15858
14432
  */
15859
14433
  _popEventContext() {
14434
+ if (this._context.earliestEvent && this._context.earliestEvent < this._context.initialTimestamp) {
14435
+ this._context.initialTimestamp = this._context.earliestEvent;
14436
+ }
14437
+
15860
14438
  const _context = {
15861
14439
  initialTimestamp: this._context.initialTimestamp,
15862
14440
  initialUrl: this._context.initialUrl,
15863
- errorIds: Array.from(this._context.errorIds),
15864
- traceIds: Array.from(this._context.traceIds),
14441
+ errorIds: Array.from(this._context.errorIds).filter(Boolean),
14442
+ traceIds: Array.from(this._context.traceIds).filter(Boolean),
15865
14443
  urls: this._context.urls,
15866
14444
  };
15867
14445
 
@@ -15900,9 +14478,6 @@ class ReplayContainer {
15900
14478
  }
15901
14479
 
15902
14480
  try {
15903
- // This uses the data from the eventBuffer, so we need to call this before `finish()
15904
- this._updateInitialTimestampFromEventBuffer();
15905
-
15906
14481
  // Note this empties the event buffer regardless of outcome of sending replay
15907
14482
  const recordingData = await this.eventBuffer.finish();
15908
14483
 
@@ -15918,6 +14493,7 @@ class ReplayContainer {
15918
14493
  replayId,
15919
14494
  recordingData,
15920
14495
  segmentId,
14496
+ includeReplayStartTimestamp: segmentId === 0,
15921
14497
  eventContext,
15922
14498
  session: this.session,
15923
14499
  options: this.getOptions(),
@@ -15929,7 +14505,7 @@ class ReplayContainer {
15929
14505
  // This means we retried 3 times and all of them failed,
15930
14506
  // or we ran into a problem we don't want to retry, like rate limiting.
15931
14507
  // In this case, we want to completely stop the replay - otherwise, we may get inconsistent segments
15932
- void this.stop('sendReplay');
14508
+ this.stop('sendReplay');
15933
14509
 
15934
14510
  const client = getCurrentHub().getClient();
15935
14511
 
@@ -15943,12 +14519,8 @@ class ReplayContainer {
15943
14519
  * Flush recording data to Sentry. Creates a lock so that only a single flush
15944
14520
  * can be active at a time. Do not call this directly.
15945
14521
  */
15946
- __init17() {this._flush = async ({
15947
- force = false,
15948
- }
15949
-
15950
- = {}) => {
15951
- if (!this._isEnabled && !force) {
14522
+ __init16() {this._flush = async () => {
14523
+ if (!this._isEnabled) {
15952
14524
  // This can happen if e.g. the replay was stopped because of exceeding the retry limit
15953
14525
  return;
15954
14526
  }
@@ -15998,7 +14570,7 @@ class ReplayContainer {
15998
14570
  }
15999
14571
 
16000
14572
  /** Handler for rrweb.record.onMutation */
16001
- __init18() {this._onMutationHandler = (mutations) => {
14573
+ __init17() {this._onMutationHandler = (mutations) => {
16002
14574
  const count = mutations.length;
16003
14575
 
16004
14576
  const mutationLimit = this._options._experiments.mutationLimit || 0;
@@ -16132,8 +14704,6 @@ function isElectronNodeRenderer() {
16132
14704
  const MEDIA_SELECTORS =
16133
14705
  'img,image,svg,video,object,picture,embed,map,audio,link[rel="icon"],link[rel="apple-touch-icon"]';
16134
14706
 
16135
- const DEFAULT_NETWORK_HEADERS = ['content-length', 'content-type', 'accept'];
16136
-
16137
14707
  let _initialized = false;
16138
14708
 
16139
14709
  /**
@@ -16174,11 +14744,6 @@ class Replay {
16174
14744
  maskAllInputs = true,
16175
14745
  blockAllMedia = true,
16176
14746
 
16177
- networkDetailAllowUrls = [],
16178
- networkCaptureBodies = true,
16179
- networkRequestHeaders = [],
16180
- networkResponseHeaders = [],
16181
-
16182
14747
  mask = [],
16183
14748
  unmask = [],
16184
14749
  block = [],
@@ -16237,13 +14802,6 @@ class Replay {
16237
14802
  errorSampleRate,
16238
14803
  useCompression,
16239
14804
  blockAllMedia,
16240
- maskAllInputs,
16241
- maskAllText,
16242
- networkDetailAllowUrls,
16243
- networkCaptureBodies,
16244
- networkRequestHeaders: _getMergedNetworkHeaders(networkRequestHeaders),
16245
- networkResponseHeaders: _getMergedNetworkHeaders(networkResponseHeaders),
16246
-
16247
14805
  _experiments,
16248
14806
  };
16249
14807
 
@@ -16297,7 +14855,14 @@ Sentry.init({ replaysOnErrorSampleRate: ${errorSampleRate} })`,
16297
14855
  }
16298
14856
 
16299
14857
  /**
16300
- * Setup and initialize replay container
14858
+ * We previously used to create a transaction in `setupOnce` and it would
14859
+ * potentially create a transaction before some native SDK integrations have run
14860
+ * and applied their own global event processor. An example is:
14861
+ * https://github.com/getsentry/sentry-javascript/blob/b47ceafbdac7f8b99093ce6023726ad4687edc48/packages/browser/src/integrations/useragent.ts
14862
+ *
14863
+ * So we call `replay.setup` in next event loop as a workaround to wait for other
14864
+ * global event processors to finish. This is no longer needed, but keeping it
14865
+ * here to avoid any future issues.
16301
14866
  */
16302
14867
  setupOnce() {
16303
14868
  if (!isBrowser()) {
@@ -16306,20 +14871,12 @@ Sentry.init({ replaysOnErrorSampleRate: ${errorSampleRate} })`,
16306
14871
 
16307
14872
  this._setup();
16308
14873
 
16309
- // Once upon a time, we tried to create a transaction in `setupOnce` and it would
16310
- // potentially create a transaction before some native SDK integrations have run
16311
- // and applied their own global event processor. An example is:
16312
- // https://github.com/getsentry/sentry-javascript/blob/b47ceafbdac7f8b99093ce6023726ad4687edc48/packages/browser/src/integrations/useragent.ts
16313
- //
16314
- // So we call `this._initialize()` in next event loop as a workaround to wait for other
16315
- // global event processors to finish. This is no longer needed, but keeping it
16316
- // here to avoid any future issues.
16317
- setTimeout(() => this._initialize());
14874
+ // XXX: See method comments above
14875
+ setTimeout(() => this.start());
16318
14876
  }
16319
14877
 
16320
14878
  /**
16321
- * Start a replay regardless of sampling rate. Calling this will always
16322
- * create a new session. Will throw an error if replay is already in progress.
14879
+ * Initializes the plugin.
16323
14880
  *
16324
14881
  * Creates or loads a session, attaches listeners to varying events (DOM,
16325
14882
  * PerformanceObserver, Recording, Sentry SDK, etc)
@@ -16332,64 +14889,27 @@ Sentry.init({ replaysOnErrorSampleRate: ${errorSampleRate} })`,
16332
14889
  this._replay.start();
16333
14890
  }
16334
14891
 
16335
- /**
16336
- * Start replay buffering. Buffers until `flush()` is called or, if
16337
- * `replaysOnErrorSampleRate` > 0, until an error occurs.
16338
- */
16339
- startBuffering() {
16340
- if (!this._replay) {
16341
- return;
16342
- }
16343
-
16344
- this._replay.startBuffering();
16345
- }
16346
-
16347
14892
  /**
16348
14893
  * Currently, this needs to be manually called (e.g. for tests). Sentry SDK
16349
14894
  * does not support a teardown
16350
14895
  */
16351
14896
  stop() {
16352
14897
  if (!this._replay) {
16353
- return Promise.resolve();
16354
- }
16355
-
16356
- return this._replay.stop();
16357
- }
16358
-
16359
- /**
16360
- * If not in "session" recording mode, flush event buffer which will create a new replay.
16361
- * Unless `continueRecording` is false, the replay will continue to record and
16362
- * behave as a "session"-based replay.
16363
- *
16364
- * Otherwise, queue up a flush.
16365
- */
16366
- flush(options) {
16367
- if (!this._replay || !this._replay.isEnabled()) {
16368
- return Promise.resolve();
14898
+ return;
16369
14899
  }
16370
14900
 
16371
- return this._replay.sendBufferedReplayOrFlush(options);
14901
+ this._replay.stop();
16372
14902
  }
16373
14903
 
16374
14904
  /**
16375
- * Get the current session ID.
14905
+ * Immediately send all pending events.
16376
14906
  */
16377
- getReplayId() {
14907
+ flush() {
16378
14908
  if (!this._replay || !this._replay.isEnabled()) {
16379
14909
  return;
16380
14910
  }
16381
14911
 
16382
- return this._replay.getSessionId();
16383
- }
16384
- /**
16385
- * Initializes replay.
16386
- */
16387
- _initialize() {
16388
- if (!this._replay) {
16389
- return;
16390
- }
16391
-
16392
- this._replay.initializeSampling();
14912
+ return this._replay.flushImmediate();
16393
14913
  }
16394
14914
 
16395
14915
  /** Setup the integration. */
@@ -16440,10 +14960,6 @@ function loadReplayOptionsFromClient(initialOptions) {
16440
14960
  return finalOptions;
16441
14961
  }
16442
14962
 
16443
- function _getMergedNetworkHeaders(headers) {
16444
- return [...DEFAULT_NETWORK_HEADERS, ...headers.map(header => header.toLowerCase())];
16445
- }
16446
-
16447
14963
  /**
16448
14964
  * Polyfill for the optional chain operator, `?.`, given previous conversion of the expression into an array of values,
16449
14965
  * descriptors, and functions.
@@ -16881,9 +15397,6 @@ class Postgres {
16881
15397
  const span = _optionalChain([parentSpan, 'optionalAccess', _6 => _6.startChild, 'call', _7 => _7({
16882
15398
  description: typeof config === 'string' ? config : (config ).text,
16883
15399
  op: 'db',
16884
- data: {
16885
- 'db.system': 'postgresql',
16886
- },
16887
15400
  })]);
16888
15401
 
16889
15402
  if (typeof callback === 'function') {
@@ -16960,9 +15473,6 @@ class Mysql {constructor() { Mysql.prototype.__init.call(this); }
16960
15473
  const span = _optionalChain([parentSpan, 'optionalAccess', _4 => _4.startChild, 'call', _5 => _5({
16961
15474
  description: typeof options === 'string' ? options : (options ).sql,
16962
15475
  op: 'db',
16963
- data: {
16964
- 'db.system': 'mysql',
16965
- },
16966
15476
  })]);
16967
15477
 
16968
15478
  if (typeof callback === 'function') {
@@ -17184,7 +15694,6 @@ class Mongo {
17184
15694
  collectionName: collection.collectionName,
17185
15695
  dbName: collection.dbName,
17186
15696
  namespace: collection.namespace,
17187
- 'db.system': 'mongodb',
17188
15697
  };
17189
15698
  const spanContext = {
17190
15699
  op: 'db',
@@ -17271,15 +15780,31 @@ class Prisma {
17271
15780
  }
17272
15781
 
17273
15782
  this._client.$use((params, next) => {
15783
+ const scope = getCurrentHub().getScope();
15784
+ const parentSpan = _optionalChain([scope, 'optionalAccess', _2 => _2.getSpan, 'call', _3 => _3()]);
15785
+
17274
15786
  const action = params.action;
17275
15787
  const model = params.model;
17276
- return trace(
17277
- { name: model ? `${model} ${action}` : action, op: 'db.sql.prisma', data: { 'db.system': 'prisma' } },
17278
- () => next(params),
17279
- );
15788
+
15789
+ const span = _optionalChain([parentSpan, 'optionalAccess', _4 => _4.startChild, 'call', _5 => _5({
15790
+ description: model ? `${model} ${action}` : action,
15791
+ op: 'db.sql.prisma',
15792
+ })]);
15793
+
15794
+ const rv = next(params);
15795
+
15796
+ if (isThenable(rv)) {
15797
+ return rv.then((res) => {
15798
+ _optionalChain([span, 'optionalAccess', _6 => _6.finish, 'call', _7 => _7()]);
15799
+ return res;
15800
+ });
15801
+ }
15802
+
15803
+ _optionalChain([span, 'optionalAccess', _8 => _8.finish, 'call', _9 => _9()]);
15804
+ return rv;
17280
15805
  });
17281
15806
  }
17282
- } Prisma.__initStatic();
15807
+ }Prisma.__initStatic();
17283
15808
 
17284
15809
  /** Tracing integration for graphql package */
17285
15810
  class GraphQL {constructor() { GraphQL.prototype.__init.call(this); }
@@ -30453,7 +28978,7 @@ const configGenerator = () => {
30453
28978
  let release;
30454
28979
  try {
30455
28980
  environment !== null && environment !== void 0 ? environment : (environment = "staging");
30456
- release !== null && release !== void 0 ? release : (release = "1.1.23-binary-004");
28981
+ release !== null && release !== void 0 ? release : (release = "1.1.23-binary-006");
30457
28982
  }
30458
28983
  catch (_a) {
30459
28984
  console.error('sentry configGenerator error');
@@ -43520,9 +42045,7 @@ var AsapAction = (function (_super) {
43520
42045
  var actions = scheduler.actions;
43521
42046
  if (id != null && ((_a = actions[actions.length - 1]) === null || _a === void 0 ? void 0 : _a.id) !== id) {
43522
42047
  immediateProvider.clearImmediate(id);
43523
- if (scheduler._scheduled === id) {
43524
- scheduler._scheduled = undefined;
43525
- }
42048
+ scheduler._scheduled = undefined;
43526
42049
  }
43527
42050
  return undefined;
43528
42051
  };