@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/esm/index.js CHANGED
@@ -947,12 +947,6 @@ function createStackParser(...parsers) {
947
947
  // Remove webpack (error: *) wrappers
948
948
  const cleanedLine = WEBPACK_ERROR_REGEXP.test(line) ? line.replace(WEBPACK_ERROR_REGEXP, '$1') : line;
949
949
 
950
- // https://github.com/getsentry/sentry-javascript/issues/7813
951
- // Skip Error: lines
952
- if (cleanedLine.match(/\S*Error: /)) {
953
- continue;
954
- }
955
-
956
950
  for (const parser of sortedParsers) {
957
951
  const frame = parser(cleanedLine);
958
952
 
@@ -1137,8 +1131,6 @@ function supportsHistory() {
1137
1131
  // eslint-disable-next-line deprecation/deprecation
1138
1132
  const WINDOW$3 = getGlobalObject();
1139
1133
 
1140
- const SENTRY_XHR_DATA_KEY = '__sentry_xhr_v2__';
1141
-
1142
1134
  /**
1143
1135
  * Instrument native APIs to call handlers that can be used to create breadcrumbs, APM spans etc.
1144
1136
  * - Console API
@@ -1251,13 +1243,11 @@ function instrumentFetch() {
1251
1243
 
1252
1244
  fill(WINDOW$3, 'fetch', function (originalFetch) {
1253
1245
  return function (...args) {
1254
- const { method, url } = parseFetchArgs(args);
1255
-
1256
1246
  const handlerData = {
1257
1247
  args,
1258
1248
  fetchData: {
1259
- method,
1260
- url,
1249
+ method: getFetchMethod(args),
1250
+ url: getFetchUrl(args),
1261
1251
  },
1262
1252
  startTimestamp: Date.now(),
1263
1253
  };
@@ -1292,53 +1282,29 @@ function instrumentFetch() {
1292
1282
  });
1293
1283
  }
1294
1284
 
1295
- function hasProp(obj, prop) {
1296
- return !!obj && typeof obj === 'object' && !!(obj )[prop];
1297
- }
1298
-
1299
- function getUrlFromResource(resource) {
1300
- if (typeof resource === 'string') {
1301
- return resource;
1302
- }
1303
-
1304
- if (!resource) {
1305
- return '';
1306
- }
1307
-
1308
- if (hasProp(resource, 'url')) {
1309
- return resource.url;
1285
+ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
1286
+ /** Extract `method` from fetch call arguments */
1287
+ function getFetchMethod(fetchArgs = []) {
1288
+ if ('Request' in WINDOW$3 && isInstanceOf(fetchArgs[0], Request) && fetchArgs[0].method) {
1289
+ return String(fetchArgs[0].method).toUpperCase();
1310
1290
  }
1311
-
1312
- if (resource.toString) {
1313
- return resource.toString();
1291
+ if (fetchArgs[1] && fetchArgs[1].method) {
1292
+ return String(fetchArgs[1].method).toUpperCase();
1314
1293
  }
1315
-
1316
- return '';
1294
+ return 'GET';
1317
1295
  }
1318
1296
 
1319
- /**
1320
- * Parses the fetch arguments to find the used Http method and the url of the request
1321
- */
1322
- function parseFetchArgs(fetchArgs) {
1323
- if (fetchArgs.length === 0) {
1324
- return { method: 'GET', url: '' };
1297
+ /** Extract `url` from fetch call arguments */
1298
+ function getFetchUrl(fetchArgs = []) {
1299
+ if (typeof fetchArgs[0] === 'string') {
1300
+ return fetchArgs[0];
1325
1301
  }
1326
-
1327
- if (fetchArgs.length === 2) {
1328
- const [url, options] = fetchArgs ;
1329
-
1330
- return {
1331
- url: getUrlFromResource(url),
1332
- method: hasProp(options, 'method') ? String(options.method).toUpperCase() : 'GET',
1333
- };
1302
+ if ('Request' in WINDOW$3 && isInstanceOf(fetchArgs[0], Request)) {
1303
+ return fetchArgs[0].url;
1334
1304
  }
1335
-
1336
- const arg = fetchArgs[0];
1337
- return {
1338
- url: getUrlFromResource(arg ),
1339
- method: hasProp(arg, 'method') ? String(arg.method).toUpperCase() : 'GET',
1340
- };
1305
+ return String(fetchArgs[0]);
1341
1306
  }
1307
+ /* eslint-enable @typescript-eslint/no-unsafe-member-access */
1342
1308
 
1343
1309
  /** JSDoc */
1344
1310
  function instrumentXHR() {
@@ -1351,11 +1317,10 @@ function instrumentXHR() {
1351
1317
  fill(xhrproto, 'open', function (originalOpen) {
1352
1318
  return function ( ...args) {
1353
1319
  const url = args[1];
1354
- const xhrInfo = (this[SENTRY_XHR_DATA_KEY] = {
1320
+ const xhrInfo = (this.__sentry_xhr__ = {
1355
1321
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
1356
1322
  method: isString$2(args[0]) ? args[0].toUpperCase() : args[0],
1357
1323
  url: args[1],
1358
- request_headers: {},
1359
1324
  });
1360
1325
 
1361
1326
  // if Sentry key appears in URL, don't capture it as a request
@@ -1366,7 +1331,7 @@ function instrumentXHR() {
1366
1331
 
1367
1332
  const onreadystatechangeHandler = () => {
1368
1333
  // For whatever reason, this is not the same instance here as from the outer method
1369
- const xhrInfo = this[SENTRY_XHR_DATA_KEY];
1334
+ const xhrInfo = this.__sentry_xhr__;
1370
1335
 
1371
1336
  if (!xhrInfo) {
1372
1337
  return;
@@ -1401,32 +1366,14 @@ function instrumentXHR() {
1401
1366
  this.addEventListener('readystatechange', onreadystatechangeHandler);
1402
1367
  }
1403
1368
 
1404
- // Intercepting `setRequestHeader` to access the request headers of XHR instance.
1405
- // This will only work for user/library defined headers, not for the default/browser-assigned headers.
1406
- // Request cookies are also unavailable for XHR, as `Cookie` header can't be defined by `setRequestHeader`.
1407
- fill(this, 'setRequestHeader', function (original) {
1408
- return function ( ...setRequestHeaderArgs) {
1409
- const [header, value] = setRequestHeaderArgs ;
1410
-
1411
- const xhrInfo = this[SENTRY_XHR_DATA_KEY];
1412
-
1413
- if (xhrInfo) {
1414
- xhrInfo.request_headers[header.toLowerCase()] = value;
1415
- }
1416
-
1417
- return original.apply(this, setRequestHeaderArgs);
1418
- };
1419
- });
1420
-
1421
1369
  return originalOpen.apply(this, args);
1422
1370
  };
1423
1371
  });
1424
1372
 
1425
1373
  fill(xhrproto, 'send', function (originalSend) {
1426
1374
  return function ( ...args) {
1427
- const sentryXhrData = this[SENTRY_XHR_DATA_KEY];
1428
- if (sentryXhrData && args[0] !== undefined) {
1429
- sentryXhrData.body = args[0];
1375
+ if (this.__sentry_xhr__ && args[0] !== undefined) {
1376
+ this.__sentry_xhr__.body = args[0];
1430
1377
  }
1431
1378
 
1432
1379
  triggerHandlers('xhr', {
@@ -1725,15 +1672,13 @@ function instrumentError() {
1725
1672
  url,
1726
1673
  });
1727
1674
 
1728
- if (_oldOnErrorHandler && !_oldOnErrorHandler.__SENTRY_LOADER__) {
1675
+ if (_oldOnErrorHandler) {
1729
1676
  // eslint-disable-next-line prefer-rest-params
1730
1677
  return _oldOnErrorHandler.apply(this, arguments);
1731
1678
  }
1732
1679
 
1733
1680
  return false;
1734
1681
  };
1735
-
1736
- WINDOW$3.onerror.__SENTRY_INSTRUMENTED__ = true;
1737
1682
  }
1738
1683
 
1739
1684
  let _oldOnUnhandledRejectionHandler = null;
@@ -1744,15 +1689,13 @@ function instrumentUnhandledRejection() {
1744
1689
  WINDOW$3.onunhandledrejection = function (e) {
1745
1690
  triggerHandlers('unhandledrejection', e);
1746
1691
 
1747
- if (_oldOnUnhandledRejectionHandler && !_oldOnUnhandledRejectionHandler.__SENTRY_LOADER__) {
1692
+ if (_oldOnUnhandledRejectionHandler) {
1748
1693
  // eslint-disable-next-line prefer-rest-params
1749
1694
  return _oldOnUnhandledRejectionHandler.apply(this, arguments);
1750
1695
  }
1751
1696
 
1752
1697
  return true;
1753
1698
  };
1754
-
1755
- WINDOW$3.onunhandledrejection.__SENTRY_INSTRUMENTED__ = true;
1756
1699
  }
1757
1700
 
1758
1701
  /* eslint-disable @typescript-eslint/no-unsafe-member-access */
@@ -2050,7 +1993,7 @@ function loadModule(moduleName) {
2050
1993
  * @returns A normalized version of the object, or `"**non-serializable**"` if any errors are thrown during normalization.
2051
1994
  */
2052
1995
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
2053
- function normalize(input, depth = 100, maxProperties = +Infinity) {
1996
+ function normalize(input, depth = +Infinity, maxProperties = +Infinity) {
2054
1997
  try {
2055
1998
  // since we're at the outermost level, we don't provide a key
2056
1999
  return visit('', input, depth, maxProperties);
@@ -2096,10 +2039,7 @@ function visit(
2096
2039
  const [memoize, unmemoize] = memo;
2097
2040
 
2098
2041
  // Get the simple cases out of the way first
2099
- if (
2100
- value == null || // this matches null and undefined -> eqeq not eqeqeq
2101
- (['number', 'boolean', 'string'].includes(typeof value) && !isNaN$1(value))
2102
- ) {
2042
+ if (value === null || (['number', 'boolean', 'string'].includes(typeof value) && !isNaN$1(value))) {
2103
2043
  return value ;
2104
2044
  }
2105
2045
 
@@ -2120,16 +2060,17 @@ function visit(
2120
2060
  return value ;
2121
2061
  }
2122
2062
 
2123
- // We can set `__sentry_override_normalization_depth__` on an object to ensure that from there
2124
- // We keep a certain amount of depth.
2125
- // This should be used sparingly, e.g. we use it for the redux integration to ensure we get a certain amount of state.
2126
- const remainingDepth =
2127
- typeof (value )['__sentry_override_normalization_depth__'] === 'number'
2128
- ? ((value )['__sentry_override_normalization_depth__'] )
2129
- : depth;
2063
+ // Do not normalize objects that we know have already been normalized. As a general rule, the
2064
+ // "__sentry_skip_normalization__" property should only be used sparingly and only should only be set on objects that
2065
+ // have already been normalized.
2066
+ let overriddenDepth = depth;
2067
+
2068
+ if (typeof (value )['__sentry_override_normalization_depth__'] === 'number') {
2069
+ overriddenDepth = (value )['__sentry_override_normalization_depth__'] ;
2070
+ }
2130
2071
 
2131
2072
  // We're also done if we've reached the max depth
2132
- if (remainingDepth === 0) {
2073
+ if (overriddenDepth === 0) {
2133
2074
  // At this point we know `serialized` is a string of the form `"[object XXXX]"`. Clean it up so it's just `"[XXXX]"`.
2134
2075
  return stringified.replace('object ', '');
2135
2076
  }
@@ -2145,7 +2086,7 @@ function visit(
2145
2086
  try {
2146
2087
  const jsonValue = valueWithToJSON.toJSON();
2147
2088
  // We need to normalize the return value of `.toJSON()` in case it has circular references
2148
- return visit('', jsonValue, remainingDepth - 1, maxProperties, memo);
2089
+ return visit('', jsonValue, overriddenDepth - 1, maxProperties, memo);
2149
2090
  } catch (err) {
2150
2091
  // pass (The built-in `toJSON` failed, but we can still try to do it ourselves)
2151
2092
  }
@@ -2174,7 +2115,7 @@ function visit(
2174
2115
 
2175
2116
  // Recursively visit all the child nodes
2176
2117
  const visitValue = visitable[visitKey];
2177
- normalized[visitKey] = visit(visitKey, visitValue, remainingDepth - 1, maxProperties, memo);
2118
+ normalized[visitKey] = visit(visitKey, visitValue, overriddenDepth - 1, maxProperties, memo);
2178
2119
 
2179
2120
  numAdded++;
2180
2121
  }
@@ -2186,7 +2127,6 @@ function visit(
2186
2127
  return normalized;
2187
2128
  }
2188
2129
 
2189
- /* eslint-disable complexity */
2190
2130
  /**
2191
2131
  * Stringify the given value. Handles various known special values and types.
2192
2132
  *
@@ -2237,6 +2177,11 @@ function stringifyValue(
2237
2177
  return '[NaN]';
2238
2178
  }
2239
2179
 
2180
+ // this catches `undefined` (but not `null`, which is a primitive and can be serialized on its own)
2181
+ if (value === void 0) {
2182
+ return '[undefined]';
2183
+ }
2184
+
2240
2185
  if (typeof value === 'function') {
2241
2186
  return `[Function: ${getFunctionName(value)}]`;
2242
2187
  }
@@ -2254,19 +2199,11 @@ function stringifyValue(
2254
2199
  // them to strings means that instances of classes which haven't defined their `toStringTag` will just come out as
2255
2200
  // `"[object Object]"`. If we instead look at the constructor's name (which is the same as the name of the class),
2256
2201
  // we can make sure that only plain objects come out that way.
2257
- const objName = getConstructorName(value);
2258
-
2259
- // Handle HTML Elements
2260
- if (/^HTML(\w*)Element$/.test(objName)) {
2261
- return `[HTMLElement: ${objName}]`;
2262
- }
2263
-
2264
- return `[object ${objName}]`;
2202
+ return `[object ${getConstructorName(value)}]`;
2265
2203
  } catch (err) {
2266
2204
  return `**non-serializable** (${err})`;
2267
2205
  }
2268
2206
  }
2269
- /* eslint-enable complexity */
2270
2207
 
2271
2208
  function getConstructorName(value) {
2272
2209
  const prototype = Object.getPrototypeOf(value);
@@ -2577,7 +2514,9 @@ function makePromiseBuffer(limit) {
2577
2514
  * // environments where DOM might not be available
2578
2515
  * @returns parsed URL object
2579
2516
  */
2580
- function parseUrl(url) {
2517
+ function parseUrl(url)
2518
+
2519
+ {
2581
2520
  if (!url) {
2582
2521
  return {};
2583
2522
  }
@@ -2595,8 +2534,6 @@ function parseUrl(url) {
2595
2534
  host: match[4],
2596
2535
  path: match[5],
2597
2536
  protocol: match[2],
2598
- search: query,
2599
- hash: fragment,
2600
2537
  relative: match[5] + query + fragment, // everything minus origin
2601
2538
  };
2602
2539
  }
@@ -2981,7 +2918,6 @@ const ITEM_TYPE_TO_DATA_CATEGORY_MAP = {
2981
2918
  profile: 'profile',
2982
2919
  replay_event: 'replay',
2983
2920
  replay_recording: 'replay',
2984
- check_in: 'monitor',
2985
2921
  };
2986
2922
 
2987
2923
  /**
@@ -3011,14 +2947,16 @@ function createEventEnvelopeHeaders(
3011
2947
  dsn,
3012
2948
  ) {
3013
2949
  const dynamicSamplingContext = event.sdkProcessingMetadata && event.sdkProcessingMetadata.dynamicSamplingContext;
2950
+
3014
2951
  return {
3015
2952
  event_id: event.event_id ,
3016
2953
  sent_at: new Date().toISOString(),
3017
2954
  ...(sdkInfo && { sdk: sdkInfo }),
3018
2955
  ...(!!tunnel && { dsn: dsnToString(dsn) }),
3019
- ...(dynamicSamplingContext && {
3020
- trace: dropUndefinedKeys({ ...dynamicSamplingContext }),
3021
- }),
2956
+ ...(event.type === 'transaction' &&
2957
+ dynamicSamplingContext && {
2958
+ trace: dropUndefinedKeys({ ...dynamicSamplingContext }),
2959
+ }),
3022
2960
  };
3023
2961
  }
3024
2962
 
@@ -3723,16 +3661,9 @@ class Scope {
3723
3661
  // errors with transaction and it relies on that.
3724
3662
  if (this._span) {
3725
3663
  event.contexts = { trace: this._span.getTraceContext(), ...event.contexts };
3726
- const transaction = this._span.transaction;
3727
- if (transaction) {
3728
- event.sdkProcessingMetadata = {
3729
- dynamicSamplingContext: transaction.getDynamicSamplingContext(),
3730
- ...event.sdkProcessingMetadata,
3731
- };
3732
- const transactionName = transaction.name;
3733
- if (transactionName) {
3734
- event.tags = { transaction: transactionName, ...event.tags };
3735
- }
3664
+ const transactionName = this._span.transaction && this._span.transaction.name;
3665
+ if (transactionName) {
3666
+ event.tags = { transaction: transactionName, ...event.tags };
3736
3667
  }
3737
3668
  }
3738
3669
 
@@ -3856,6 +3787,11 @@ const API_VERSION = 4;
3856
3787
  */
3857
3788
  const DEFAULT_BREADCRUMBS = 100;
3858
3789
 
3790
+ /**
3791
+ * A layer in the process stack.
3792
+ * @hidden
3793
+ */
3794
+
3859
3795
  /**
3860
3796
  * @inheritDoc
3861
3797
  */
@@ -4133,17 +4069,7 @@ class Hub {
4133
4069
  * @inheritDoc
4134
4070
  */
4135
4071
  startTransaction(context, customSamplingContext) {
4136
- const result = this._callExtensionMethod('startTransaction', context, customSamplingContext);
4137
-
4138
- if ((typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && !result) {
4139
- // eslint-disable-next-line no-console
4140
- console.warn(`Tracing extension 'startTransaction' has not been added. Call 'addTracingExtensions' before calling 'init':
4141
- Sentry.addTracingExtensions();
4142
- Sentry.init({...});
4143
- `);
4144
- }
4145
-
4146
- return result;
4072
+ return this._callExtensionMethod('startTransaction', context, customSamplingContext);
4147
4073
  }
4148
4074
 
4149
4075
  /**
@@ -4228,10 +4154,13 @@ Sentry.init({...});
4228
4154
  */
4229
4155
  _sendSessionUpdate() {
4230
4156
  const { scope, client } = this.getStackTop();
4157
+ if (!scope) return;
4231
4158
 
4232
4159
  const session = scope.getSession();
4233
- if (session && client && client.captureSession) {
4234
- client.captureSession(session);
4160
+ if (session) {
4161
+ if (client && client.captureSession) {
4162
+ client.captureSession(session);
4163
+ }
4235
4164
  }
4236
4165
  }
4237
4166
 
@@ -4301,28 +4230,47 @@ function getCurrentHub() {
4301
4230
  // Get main carrier (global for every environment)
4302
4231
  const registry = getMainCarrier();
4303
4232
 
4304
- if (registry.__SENTRY__ && registry.__SENTRY__.acs) {
4305
- const hub = registry.__SENTRY__.acs.getCurrentHub();
4306
-
4307
- if (hub) {
4308
- return hub;
4309
- }
4310
- }
4311
-
4312
- // Return hub that lives on a global object
4313
- return getGlobalHub(registry);
4314
- }
4315
-
4316
- function getGlobalHub(registry = getMainCarrier()) {
4317
4233
  // If there's no hub, or its an old API, assign a new one
4318
4234
  if (!hasHubOnCarrier(registry) || getHubFromCarrier(registry).isOlderThan(API_VERSION)) {
4319
4235
  setHubOnCarrier(registry, new Hub());
4320
4236
  }
4321
4237
 
4238
+ // Prefer domains over global if they are there (applicable only to Node environment)
4239
+ if (isNodeEnv()) {
4240
+ return getHubFromActiveDomain(registry);
4241
+ }
4322
4242
  // Return hub that lives on a global object
4323
4243
  return getHubFromCarrier(registry);
4324
4244
  }
4325
4245
 
4246
+ /**
4247
+ * Try to read the hub from an active domain, and fallback to the registry if one doesn't exist
4248
+ * @returns discovered hub
4249
+ */
4250
+ function getHubFromActiveDomain(registry) {
4251
+ try {
4252
+ const sentry = getMainCarrier().__SENTRY__;
4253
+ const activeDomain = sentry && sentry.extensions && sentry.extensions.domain && sentry.extensions.domain.active;
4254
+
4255
+ // If there's no active domain, just return global hub
4256
+ if (!activeDomain) {
4257
+ return getHubFromCarrier(registry);
4258
+ }
4259
+
4260
+ // If there's no hub on current domain, or it's an old API, assign a new one
4261
+ if (!hasHubOnCarrier(activeDomain) || getHubFromCarrier(activeDomain).isOlderThan(API_VERSION)) {
4262
+ const registryHubTopStack = getHubFromCarrier(registry).getStackTop();
4263
+ setHubOnCarrier(activeDomain, new Hub(registryHubTopStack.client, Scope.clone(registryHubTopStack.scope)));
4264
+ }
4265
+
4266
+ // Return hub that lives on a domain
4267
+ return getHubFromCarrier(activeDomain);
4268
+ } catch (_Oo) {
4269
+ // Return hub that lives on a global object
4270
+ return getHubFromCarrier(registry);
4271
+ }
4272
+ }
4273
+
4326
4274
  /**
4327
4275
  * This will tell whether a carrier has a hub on it or not
4328
4276
  * @param carrier object
@@ -4396,69 +4344,6 @@ var SpanStatus; (function (SpanStatus) {
4396
4344
  const DataLoss = 'data_loss'; SpanStatus["DataLoss"] = DataLoss;
4397
4345
  })(SpanStatus || (SpanStatus = {}));
4398
4346
 
4399
- /**
4400
- * Wraps a function with a transaction/span and finishes the span after the function is done.
4401
- *
4402
- * Note that if you have not enabled tracing extensions via `addTracingExtensions`, this function
4403
- * will not generate spans, and the `span` returned from the callback may be undefined.
4404
- *
4405
- * This function is meant to be used internally and may break at any time. Use at your own risk.
4406
- *
4407
- * @internal
4408
- * @private
4409
- */
4410
- function trace(
4411
- context,
4412
- callback,
4413
- // eslint-disable-next-line @typescript-eslint/no-empty-function
4414
- onError = () => {},
4415
- ) {
4416
- const ctx = { ...context };
4417
- // If a name is set and a description is not, set the description to the name.
4418
- if (ctx.name !== undefined && ctx.description === undefined) {
4419
- ctx.description = ctx.name;
4420
- }
4421
-
4422
- const hub = getCurrentHub();
4423
- const scope = hub.getScope();
4424
-
4425
- const parentSpan = scope.getSpan();
4426
- const activeSpan = parentSpan ? parentSpan.startChild(ctx) : hub.startTransaction(ctx);
4427
- scope.setSpan(activeSpan);
4428
-
4429
- function finishAndSetSpan() {
4430
- activeSpan && activeSpan.finish();
4431
- hub.getScope().setSpan(parentSpan);
4432
- }
4433
-
4434
- let maybePromiseResult;
4435
- try {
4436
- maybePromiseResult = callback(activeSpan);
4437
- } catch (e) {
4438
- activeSpan && activeSpan.setStatus('internal_error');
4439
- onError(e);
4440
- finishAndSetSpan();
4441
- throw e;
4442
- }
4443
-
4444
- if (isThenable(maybePromiseResult)) {
4445
- Promise.resolve(maybePromiseResult).then(
4446
- () => {
4447
- finishAndSetSpan();
4448
- },
4449
- e => {
4450
- activeSpan && activeSpan.setStatus('internal_error');
4451
- onError(e);
4452
- finishAndSetSpan();
4453
- },
4454
- );
4455
- } else {
4456
- finishAndSetSpan();
4457
- }
4458
-
4459
- return maybePromiseResult;
4460
- }
4461
-
4462
4347
  // Note: All functions in this file are typed with a return value of `ReturnType<Hub[HUB_FUNCTION]>`,
4463
4348
  // where HUB_FUNCTION is some method on the Hub class.
4464
4349
  //
@@ -4800,11 +4685,7 @@ function prepareEvent(
4800
4685
 
4801
4686
  applyClientOptions(prepared, options);
4802
4687
  applyIntegrationsMetadata(prepared, integrations);
4803
-
4804
- // Only apply debug metadata to error events.
4805
- if (event.type === undefined) {
4806
- applyDebugMetadata(prepared, options.stackParser);
4807
- }
4688
+ applyDebugMetadata(prepared, options.stackParser);
4808
4689
 
4809
4690
  // If we have scope given to us, use it as the base for further modifications.
4810
4691
  // This allows us to prevent unnecessary copying of data if `captureContext` is not provided.
@@ -4881,8 +4762,6 @@ function applyClientOptions(event, options) {
4881
4762
  }
4882
4763
  }
4883
4764
 
4884
- const debugIdStackParserCache = new WeakMap();
4885
-
4886
4765
  /**
4887
4766
  * Applies debug metadata images to the event in order to apply source maps by looking up their debug ID.
4888
4767
  */
@@ -4893,28 +4772,10 @@ function applyDebugMetadata(event, stackParser) {
4893
4772
  return;
4894
4773
  }
4895
4774
 
4896
- let debugIdStackFramesCache;
4897
- const cachedDebugIdStackFrameCache = debugIdStackParserCache.get(stackParser);
4898
- if (cachedDebugIdStackFrameCache) {
4899
- debugIdStackFramesCache = cachedDebugIdStackFrameCache;
4900
- } else {
4901
- debugIdStackFramesCache = new Map();
4902
- debugIdStackParserCache.set(stackParser, debugIdStackFramesCache);
4903
- }
4904
-
4905
4775
  // Build a map of filename -> debug_id
4906
4776
  const filenameDebugIdMap = Object.keys(debugIdMap).reduce((acc, debugIdStackTrace) => {
4907
- let parsedStack;
4908
- const cachedParsedStack = debugIdStackFramesCache.get(debugIdStackTrace);
4909
- if (cachedParsedStack) {
4910
- parsedStack = cachedParsedStack;
4911
- } else {
4912
- parsedStack = stackParser(debugIdStackTrace);
4913
- debugIdStackFramesCache.set(debugIdStackTrace, parsedStack);
4914
- }
4915
-
4916
- for (let i = parsedStack.length - 1; i >= 0; i--) {
4917
- const stackFrame = parsedStack[i];
4777
+ const parsedStack = stackParser(debugIdStackTrace);
4778
+ for (const stackFrame of parsedStack) {
4918
4779
  if (stackFrame.filename) {
4919
4780
  acc[stackFrame.filename] = debugIdMap[debugIdStackTrace];
4920
4781
  break;
@@ -5819,7 +5680,7 @@ function getEventForEnvelopeItem(item, type) {
5819
5680
  return Array.isArray(item) ? (item )[1] : undefined;
5820
5681
  }
5821
5682
 
5822
- const SDK_VERSION = '7.52.1';
5683
+ const SDK_VERSION = '7.46.0';
5823
5684
 
5824
5685
  let originalFunctionToString;
5825
5686
 
@@ -5842,17 +5703,11 @@ class FunctionToString {constructor() { FunctionToString.prototype.__init.call(
5842
5703
  // eslint-disable-next-line @typescript-eslint/unbound-method
5843
5704
  originalFunctionToString = Function.prototype.toString;
5844
5705
 
5845
- // intrinsics (like Function.prototype) might be immutable in some environments
5846
- // e.g. Node with --frozen-intrinsics, XS (an embedded JavaScript engine) or SES (a JavaScript proposal)
5847
- try {
5848
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
5849
- Function.prototype.toString = function ( ...args) {
5850
- const context = getOriginalFunction(this) || this;
5851
- return originalFunctionToString.apply(context, args);
5852
- };
5853
- } catch (e) {
5854
- // ignore errors here, just don't patch this
5855
- }
5706
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
5707
+ Function.prototype.toString = function ( ...args) {
5708
+ const context = getOriginalFunction(this) || this;
5709
+ return originalFunctionToString.apply(context, args);
5710
+ };
5856
5711
  }
5857
5712
  } FunctionToString.__initStatic();
5858
5713
 
@@ -6000,9 +5855,8 @@ function _getPossibleEventMessages(event) {
6000
5855
  return [event.message];
6001
5856
  }
6002
5857
  if (event.exception) {
6003
- const { values } = event.exception;
6004
5858
  try {
6005
- const { type = '', value = '' } = (values && values[values.length - 1]) || {};
5859
+ const { type = '', value = '' } = (event.exception.values && event.exception.values[0]) || {};
6006
5860
  return [`${value}`, `${type}: ${value}`];
6007
5861
  } catch (oO) {
6008
5862
  (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.error(`Cannot extract message for event ${getEventDescription(event)}`);
@@ -6661,14 +6515,12 @@ function _consoleBreadcrumb(handlerData) {
6661
6515
  function _xhrBreadcrumb(handlerData) {
6662
6516
  const { startTimestamp, endTimestamp } = handlerData;
6663
6517
 
6664
- const sentryXhrData = handlerData.xhr[SENTRY_XHR_DATA_KEY];
6665
-
6666
6518
  // We only capture complete, non-sentry requests
6667
- if (!startTimestamp || !endTimestamp || !sentryXhrData) {
6519
+ if (!startTimestamp || !endTimestamp || !handlerData.xhr.__sentry_xhr__) {
6668
6520
  return;
6669
6521
  }
6670
6522
 
6671
- const { method, url, status_code, body } = sentryXhrData;
6523
+ const { method, url, status_code, body } = handlerData.xhr.__sentry_xhr__;
6672
6524
 
6673
6525
  const data = {
6674
6526
  method,
@@ -6786,43 +6638,6 @@ function _isEvent(event) {
6786
6638
  return event && !!(event ).target;
6787
6639
  }
6788
6640
 
6789
- /**
6790
- * Creates an envelope from a user feedback.
6791
- */
6792
- function createUserFeedbackEnvelope(
6793
- feedback,
6794
- {
6795
- metadata,
6796
- tunnel,
6797
- dsn,
6798
- }
6799
-
6800
- ,
6801
- ) {
6802
- const headers = {
6803
- event_id: feedback.event_id,
6804
- sent_at: new Date().toISOString(),
6805
- ...(metadata &&
6806
- metadata.sdk && {
6807
- sdk: {
6808
- name: metadata.sdk.name,
6809
- version: metadata.sdk.version,
6810
- },
6811
- }),
6812
- ...(!!tunnel && !!dsn && { dsn: dsnToString(dsn) }),
6813
- };
6814
- const item = createUserFeedbackEnvelopeItem(feedback);
6815
-
6816
- return createEnvelope(headers, [item]);
6817
- }
6818
-
6819
- function createUserFeedbackEnvelopeItem(feedback) {
6820
- const feedbackHeaders = {
6821
- type: 'user_report',
6822
- };
6823
- return [feedbackHeaders, feedback];
6824
- }
6825
-
6826
6641
  /**
6827
6642
  * Configuration options for the Sentry Browser SDK.
6828
6643
  * @see @sentry/types Options for more information.
@@ -6905,23 +6720,6 @@ class BrowserClient extends BaseClient {
6905
6720
  super.sendEvent(event, hint);
6906
6721
  }
6907
6722
 
6908
- /**
6909
- * Sends user feedback to Sentry.
6910
- */
6911
- captureUserFeedback(feedback) {
6912
- if (!this._isEnabled()) {
6913
- (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.warn('SDK not enabled, will not capture user feedback.');
6914
- return;
6915
- }
6916
-
6917
- const envelope = createUserFeedbackEnvelope(feedback, {
6918
- metadata: this.getSdkMetadata(),
6919
- dsn: this.getDsn(),
6920
- tunnel: this.getOptions().tunnel,
6921
- });
6922
- void this._sendEnvelope(envelope);
6923
- }
6924
-
6925
6723
  /**
6926
6724
  * @inheritDoc
6927
6725
  */
@@ -7164,7 +6962,7 @@ function createFrame(filename, func, lineno, colno) {
7164
6962
 
7165
6963
  // Chromium based browsers: Chrome, Brave, new Opera, new Edge
7166
6964
  const chromeRegex =
7167
- /^\s*at (?:(.+?\)(?: \[.+\])?|.*?) ?\((?:address at )?)?(?:async )?((?:<anonymous>|[-a-z]+:|.*bundle|\/)?.*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i;
6965
+ /^\s*at (?:(.*\).*?|.*?) ?\((?:address at )?)?(?:async )?((?:file|https?|blob|chrome-extension|address|native|eval|webpack|<anonymous>|[-a-z]+:|.*bundle|\/)?.*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i;
7168
6966
  const chromeEvalRegex = /\((\S*)(?::(\d+))(?::(\d+))\)/;
7169
6967
 
7170
6968
  const chrome = line => {
@@ -7200,7 +6998,7 @@ const chromeStackLineParser = [CHROME_PRIORITY, chrome];
7200
6998
  // generates filenames without a prefix like `file://` the filenames in the stacktrace are just 42.js
7201
6999
  // We need this specific case for now because we want no other regex to match.
7202
7000
  const geckoREgex =
7203
- /^\s*(.*?)(?:\((.*?)\))?(?:^|@)?((?:[-a-z]+)?:\/.*?|\[native code\]|[^@]*(?:bundle|\d+\.js)|\/[\w\-. /=]+)(?::(\d+))?(?::(\d+))?\s*$/i;
7001
+ /^\s*(.*?)(?:\((.*?)\))?(?:^|@)?((?:file|https?|blob|chrome|webpack|resource|moz-extension|safari-extension|safari-web-extension|capacitor)?:\/.*?|\[native code\]|[^@]*(?:bundle|\d+\.js)|\/[\w\-. /=]+)(?::(\d+))?(?::(\d+))?\s*$/i;
7204
7002
  const geckoEvalRegex = /(\S+) line (\d+)(?: > eval line \d+)* > eval/i;
7205
7003
 
7206
7004
  const gecko = line => {
@@ -7232,7 +7030,8 @@ const gecko = line => {
7232
7030
 
7233
7031
  const geckoStackLineParser = [GECKO_PRIORITY, gecko];
7234
7032
 
7235
- const winjsRegex = /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:[-a-z]+):.*?):(\d+)(?::(\d+))?\)?\s*$/i;
7033
+ const winjsRegex =
7034
+ /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:file|ms-appx|https?|webpack|blob):.*?):(\d+)(?::(\d+))?\)?\s*$/i;
7236
7035
 
7237
7036
  const winjs = line => {
7238
7037
  const parts = winjsRegex.exec(line);
@@ -8297,14 +8096,11 @@ const REPLAY_SESSION_KEY = 'sentryReplaySession';
8297
8096
  const REPLAY_EVENT_NAME = 'replay_event';
8298
8097
  const UNABLE_TO_SEND_REPLAY = 'Unable to send Replay';
8299
8098
 
8300
- // The idle limit for a session after which recording is paused.
8301
- const SESSION_IDLE_PAUSE_DURATION = 300000; // 5 minutes in ms
8302
-
8303
- // The idle limit for a session after which the session expires.
8304
- const SESSION_IDLE_EXPIRE_DURATION = 900000; // 15 minutes in ms
8099
+ // The idle limit for a session
8100
+ const SESSION_IDLE_DURATION = 300000; // 5 minutes in ms
8305
8101
 
8306
8102
  // The maximum length of a session
8307
- const MAX_SESSION_LIFE = 3600000; // 60 minutes in ms
8103
+ const MAX_SESSION_LIFE = 3600000; // 60 minutes
8308
8104
 
8309
8105
  /** Default flush delays */
8310
8106
  const DEFAULT_FLUSH_MIN_DELAY = 5000;
@@ -8313,16 +8109,13 @@ const DEFAULT_FLUSH_MIN_DELAY = 5000;
8313
8109
  const DEFAULT_FLUSH_MAX_DELAY = 5500;
8314
8110
 
8315
8111
  /* How long to wait for error checkouts */
8316
- const BUFFER_CHECKOUT_TIME = 60000;
8112
+ const ERROR_CHECKOUT_TIME = 60000;
8317
8113
 
8318
8114
  const RETRY_BASE_INTERVAL = 5000;
8319
8115
  const RETRY_MAX_COUNT = 3;
8320
8116
 
8321
- /* The max (uncompressed) size in bytes of a network body. Any body larger than this will be truncated. */
8322
- const NETWORK_BODY_MAX_SIZE = 150000;
8323
-
8324
- /* The max size of a single console arg that is captured. Any arg larger than this will be truncated. */
8325
- const CONSOLE_ARG_MAX_SIZE = 5000;
8117
+ /* The max (uncompressed) size in bytes of a network body. Any body larger than this will be dropped. */
8118
+ const NETWORK_BODY_MAX_SIZE = 300000;
8326
8119
 
8327
8120
  var NodeType$1;
8328
8121
  (function (NodeType) {
@@ -8359,7 +8152,7 @@ function maskInputValue({ input, maskInputSelector, unmaskInputSelector, maskInp
8359
8152
  if (unmaskInputSelector && input.matches(unmaskInputSelector)) {
8360
8153
  return text;
8361
8154
  }
8362
- if (input.hasAttribute('data-rr-is-password')) {
8155
+ if (input.hasAttribute('rr_is_password')) {
8363
8156
  type = 'password';
8364
8157
  }
8365
8158
  if (isInputTypeMasked({ maskInputOptions, tagName, type }) ||
@@ -8392,21 +8185,6 @@ function is2DCanvasBlank(canvas) {
8392
8185
  }
8393
8186
  return true;
8394
8187
  }
8395
- function getInputType(element) {
8396
- const type = element.type;
8397
- return element.hasAttribute('data-rr-is-password')
8398
- ? 'password'
8399
- : type
8400
- ? type.toLowerCase()
8401
- : null;
8402
- }
8403
- function getInputValue(el, tagName, type) {
8404
- typeof type === 'string' ? type.toLowerCase() : '';
8405
- if (tagName === 'INPUT' && (type === 'radio' || type === 'checkbox')) {
8406
- return el.getAttribute('value') || '';
8407
- }
8408
- return el.value;
8409
- }
8410
8188
 
8411
8189
  let _id = 1;
8412
8190
  const tagNameRegex = new RegExp('[^a-z0-9-_:]');
@@ -8445,13 +8223,6 @@ function getCssRuleString(rule) {
8445
8223
  catch (_a) {
8446
8224
  }
8447
8225
  }
8448
- return validateStringifiedCssRule(cssStringified);
8449
- }
8450
- function validateStringifiedCssRule(cssStringified) {
8451
- if (cssStringified.indexOf(':') > -1) {
8452
- const regex = /(\[(?:[\w-]+)[^\\])(:(?:[\w-]+)\])/gm;
8453
- return cssStringified.replace(regex, '$1\\$2');
8454
- }
8455
8226
  return cssStringified;
8456
8227
  }
8457
8228
  function isCSSImportRule(rule) {
@@ -8460,7 +8231,7 @@ function isCSSImportRule(rule) {
8460
8231
  function stringifyStyleSheet(sheet) {
8461
8232
  return sheet.cssRules
8462
8233
  ? Array.from(sheet.cssRules)
8463
- .map((rule) => rule.cssText ? validateStringifiedCssRule(rule.cssText) : '')
8234
+ .map((rule) => rule.cssText || '')
8464
8235
  .join('')
8465
8236
  : '';
8466
8237
  }
@@ -8795,15 +8566,14 @@ function serializeNode(n, options) {
8795
8566
  tagName === 'select' ||
8796
8567
  tagName === 'option') {
8797
8568
  const el = n;
8798
- const type = getInputType(el);
8799
- const value = getInputValue(el, tagName.toUpperCase(), type);
8569
+ const value = getInputValue(tagName, el, attributes);
8800
8570
  const checked = n.checked;
8801
- if (type !== 'submit' &&
8802
- type !== 'button' &&
8571
+ if (attributes.type !== 'submit' &&
8572
+ attributes.type !== 'button' &&
8803
8573
  value) {
8804
8574
  attributes.value = maskInputValue({
8805
8575
  input: el,
8806
- type,
8576
+ type: attributes.type,
8807
8577
  tagName,
8808
8578
  value,
8809
8579
  maskInputSelector,
@@ -9276,8 +9046,15 @@ function snapshot(n, options) {
9276
9046
  function skipAttribute(tagName, attributeName, value) {
9277
9047
  return ((tagName === 'video' || tagName === 'audio') && attributeName === 'autoplay');
9278
9048
  }
9049
+ function getInputValue(tagName, el, attributes) {
9050
+ if (tagName === 'input' &&
9051
+ (attributes.type === 'radio' || attributes.type === 'checkbox')) {
9052
+ return el.getAttribute('value') || '';
9053
+ }
9054
+ return el.value;
9055
+ }
9279
9056
 
9280
- var EventType$1;
9057
+ var EventType;
9281
9058
  (function (EventType) {
9282
9059
  EventType[EventType["DomContentLoaded"] = 0] = "DomContentLoaded";
9283
9060
  EventType[EventType["Load"] = 1] = "Load";
@@ -9286,7 +9063,7 @@ var EventType$1;
9286
9063
  EventType[EventType["Meta"] = 4] = "Meta";
9287
9064
  EventType[EventType["Custom"] = 5] = "Custom";
9288
9065
  EventType[EventType["Plugin"] = 6] = "Plugin";
9289
- })(EventType$1 || (EventType$1 = {}));
9066
+ })(EventType || (EventType = {}));
9290
9067
  var IncrementalSource;
9291
9068
  (function (IncrementalSource) {
9292
9069
  IncrementalSource[IncrementalSource["Mutation"] = 0] = "Mutation";
@@ -9901,9 +9678,9 @@ class MutationBuffer {
9901
9678
  this.attributes.push(item);
9902
9679
  }
9903
9680
  if (m.attributeName === 'type' &&
9904
- target.tagName === 'INPUT' &&
9681
+ m.target.tagName === 'INPUT' &&
9905
9682
  (m.oldValue || '').toLowerCase() === 'password') {
9906
- target.setAttribute('data-rr-is-password', 'true');
9683
+ m.target.setAttribute('rr_is_password', 'true');
9907
9684
  }
9908
9685
  if (m.attributeName === 'style') {
9909
9686
  const old = this.doc.createElement('span');
@@ -10300,25 +10077,27 @@ function initInputObserver({ inputCb, doc, mirror, blockClass, blockSelector, un
10300
10077
  isBlocked(target, blockClass, blockSelector, unblockSelector)) {
10301
10078
  return;
10302
10079
  }
10303
- const el = target;
10304
- const type = getInputType(el);
10305
- if (el.classList.contains(ignoreClass) ||
10306
- (ignoreSelector && el.matches(ignoreSelector))) {
10080
+ let type = target.type;
10081
+ if (target.classList.contains(ignoreClass) ||
10082
+ (ignoreSelector && target.matches(ignoreSelector))) {
10307
10083
  return;
10308
10084
  }
10309
- let text = getInputValue(el, tagName, type);
10085
+ let text = target.value;
10310
10086
  let isChecked = false;
10087
+ if (target.hasAttribute('rr_is_password')) {
10088
+ type = 'password';
10089
+ }
10311
10090
  if (type === 'radio' || type === 'checkbox') {
10312
10091
  isChecked = target.checked;
10313
10092
  }
10314
- if (hasInputMaskOptions({
10093
+ else if (hasInputMaskOptions({
10315
10094
  maskInputOptions,
10316
10095
  maskInputSelector,
10317
10096
  tagName,
10318
10097
  type,
10319
10098
  })) {
10320
10099
  text = maskInputValue({
10321
- input: el,
10100
+ input: target,
10322
10101
  maskInputOptions,
10323
10102
  maskInputSelector,
10324
10103
  unmaskInputSelector,
@@ -10335,18 +10114,8 @@ function initInputObserver({ inputCb, doc, mirror, blockClass, blockSelector, un
10335
10114
  .querySelectorAll(`input[type="radio"][name="${name}"]`)
10336
10115
  .forEach((el) => {
10337
10116
  if (el !== target) {
10338
- const text = maskInputValue({
10339
- input: el,
10340
- maskInputOptions,
10341
- maskInputSelector,
10342
- unmaskInputSelector,
10343
- tagName,
10344
- type,
10345
- value: getInputValue(el, tagName, type),
10346
- maskInputFn,
10347
- });
10348
10117
  cbWithDedup(el, callbackWrapper(wrapEventWithUserTriggeredFlag)({
10349
- text,
10118
+ text: el.value,
10350
10119
  isChecked: !isChecked,
10351
10120
  userTriggered: false,
10352
10121
  }, userTriggeredOnInput));
@@ -11259,17 +11028,17 @@ function record(options = {}) {
11259
11028
  wrappedEmit = (e, isCheckout) => {
11260
11029
  var _a;
11261
11030
  if (((_a = mutationBuffers[0]) === null || _a === void 0 ? void 0 : _a.isFrozen()) &&
11262
- e.type !== EventType$1.FullSnapshot &&
11263
- !(e.type === EventType$1.IncrementalSnapshot &&
11031
+ e.type !== EventType.FullSnapshot &&
11032
+ !(e.type === EventType.IncrementalSnapshot &&
11264
11033
  e.data.source === IncrementalSource.Mutation)) {
11265
11034
  mutationBuffers.forEach((buf) => buf.unfreeze());
11266
11035
  }
11267
11036
  emit(eventProcessor(e), isCheckout);
11268
- if (e.type === EventType$1.FullSnapshot) {
11037
+ if (e.type === EventType.FullSnapshot) {
11269
11038
  lastFullSnapshotEvent = e;
11270
11039
  incrementalSnapshotCount = 0;
11271
11040
  }
11272
- else if (e.type === EventType$1.IncrementalSnapshot) {
11041
+ else if (e.type === EventType.IncrementalSnapshot) {
11273
11042
  if (e.data.source === IncrementalSource.Mutation &&
11274
11043
  e.data.isAttachIframe) {
11275
11044
  return;
@@ -11285,16 +11054,16 @@ function record(options = {}) {
11285
11054
  };
11286
11055
  const wrappedMutationEmit = (m) => {
11287
11056
  wrappedEmit(wrapEvent({
11288
- type: EventType$1.IncrementalSnapshot,
11057
+ type: EventType.IncrementalSnapshot,
11289
11058
  data: Object.assign({ source: IncrementalSource.Mutation }, m),
11290
11059
  }));
11291
11060
  };
11292
11061
  const wrappedScrollEmit = (p) => wrappedEmit(wrapEvent({
11293
- type: EventType$1.IncrementalSnapshot,
11062
+ type: EventType.IncrementalSnapshot,
11294
11063
  data: Object.assign({ source: IncrementalSource.Scroll }, p),
11295
11064
  }));
11296
11065
  const wrappedCanvasMutationEmit = (p) => wrappedEmit(wrapEvent({
11297
- type: EventType$1.IncrementalSnapshot,
11066
+ type: EventType.IncrementalSnapshot,
11298
11067
  data: Object.assign({ source: IncrementalSource.CanvasMutation }, p),
11299
11068
  }));
11300
11069
  const iframeManager = new IframeManager({
@@ -11339,7 +11108,7 @@ function record(options = {}) {
11339
11108
  takeFullSnapshot = (isCheckout = false) => {
11340
11109
  var _a, _b, _c, _d;
11341
11110
  wrappedEmit(wrapEvent({
11342
- type: EventType$1.Meta,
11111
+ type: EventType.Meta,
11343
11112
  data: {
11344
11113
  href: window.location.href,
11345
11114
  width: getWindowWidth(),
@@ -11382,7 +11151,7 @@ function record(options = {}) {
11382
11151
  }
11383
11152
  mirror.map = idNodeMap;
11384
11153
  wrappedEmit(wrapEvent({
11385
- type: EventType$1.FullSnapshot,
11154
+ type: EventType.FullSnapshot,
11386
11155
  data: {
11387
11156
  node,
11388
11157
  initialOffset: {
@@ -11407,7 +11176,7 @@ function record(options = {}) {
11407
11176
  const handlers = [];
11408
11177
  handlers.push(on$1('DOMContentLoaded', () => {
11409
11178
  wrappedEmit(wrapEvent({
11410
- type: EventType$1.DomContentLoaded,
11179
+ type: EventType.DomContentLoaded,
11411
11180
  data: {},
11412
11181
  }));
11413
11182
  }));
@@ -11417,40 +11186,40 @@ function record(options = {}) {
11417
11186
  onMutation,
11418
11187
  mutationCb: wrappedMutationEmit,
11419
11188
  mousemoveCb: (positions, source) => wrappedEmit(wrapEvent({
11420
- type: EventType$1.IncrementalSnapshot,
11189
+ type: EventType.IncrementalSnapshot,
11421
11190
  data: {
11422
11191
  source,
11423
11192
  positions,
11424
11193
  },
11425
11194
  })),
11426
11195
  mouseInteractionCb: (d) => wrappedEmit(wrapEvent({
11427
- type: EventType$1.IncrementalSnapshot,
11196
+ type: EventType.IncrementalSnapshot,
11428
11197
  data: Object.assign({ source: IncrementalSource.MouseInteraction }, d),
11429
11198
  })),
11430
11199
  scrollCb: wrappedScrollEmit,
11431
11200
  viewportResizeCb: (d) => wrappedEmit(wrapEvent({
11432
- type: EventType$1.IncrementalSnapshot,
11201
+ type: EventType.IncrementalSnapshot,
11433
11202
  data: Object.assign({ source: IncrementalSource.ViewportResize }, d),
11434
11203
  })),
11435
11204
  inputCb: (v) => wrappedEmit(wrapEvent({
11436
- type: EventType$1.IncrementalSnapshot,
11205
+ type: EventType.IncrementalSnapshot,
11437
11206
  data: Object.assign({ source: IncrementalSource.Input }, v),
11438
11207
  })),
11439
11208
  mediaInteractionCb: (p) => wrappedEmit(wrapEvent({
11440
- type: EventType$1.IncrementalSnapshot,
11209
+ type: EventType.IncrementalSnapshot,
11441
11210
  data: Object.assign({ source: IncrementalSource.MediaInteraction }, p),
11442
11211
  })),
11443
11212
  styleSheetRuleCb: (r) => wrappedEmit(wrapEvent({
11444
- type: EventType$1.IncrementalSnapshot,
11213
+ type: EventType.IncrementalSnapshot,
11445
11214
  data: Object.assign({ source: IncrementalSource.StyleSheetRule }, r),
11446
11215
  })),
11447
11216
  styleDeclarationCb: (r) => wrappedEmit(wrapEvent({
11448
- type: EventType$1.IncrementalSnapshot,
11217
+ type: EventType.IncrementalSnapshot,
11449
11218
  data: Object.assign({ source: IncrementalSource.StyleDeclaration }, r),
11450
11219
  })),
11451
11220
  canvasMutationCb: wrappedCanvasMutationEmit,
11452
11221
  fontCb: (p) => wrappedEmit(wrapEvent({
11453
- type: EventType$1.IncrementalSnapshot,
11222
+ type: EventType.IncrementalSnapshot,
11454
11223
  data: Object.assign({ source: IncrementalSource.Font }, p),
11455
11224
  })),
11456
11225
  blockClass,
@@ -11483,7 +11252,7 @@ function record(options = {}) {
11483
11252
  observer: p.observer,
11484
11253
  options: p.options,
11485
11254
  callback: (payload) => wrappedEmit(wrapEvent({
11486
- type: EventType$1.Plugin,
11255
+ type: EventType.Plugin,
11487
11256
  data: {
11488
11257
  plugin: p.name,
11489
11258
  payload,
@@ -11511,7 +11280,7 @@ function record(options = {}) {
11511
11280
  else {
11512
11281
  handlers.push(on$1('load', () => {
11513
11282
  wrappedEmit(wrapEvent({
11514
- type: EventType$1.Load,
11283
+ type: EventType.Load,
11515
11284
  data: {},
11516
11285
  }));
11517
11286
  init();
@@ -11530,7 +11299,7 @@ record.addCustomEvent = (tag, payload) => {
11530
11299
  throw new Error('please add custom event after start recording');
11531
11300
  }
11532
11301
  wrappedEmit(wrapEvent({
11533
- type: EventType$1.Custom,
11302
+ type: EventType.Custom,
11534
11303
  data: {
11535
11304
  tag,
11536
11305
  payload,
@@ -11548,475 +11317,6 @@ record.takeFullSnapshot = (isCheckout) => {
11548
11317
  };
11549
11318
  record.mirror = mirror;
11550
11319
 
11551
- /**
11552
- * Create a breadcrumb for a replay.
11553
- */
11554
- function createBreadcrumb(
11555
- breadcrumb,
11556
- ) {
11557
- return {
11558
- timestamp: Date.now() / 1000,
11559
- type: 'default',
11560
- ...breadcrumb,
11561
- };
11562
- }
11563
-
11564
- var NodeType;
11565
- (function (NodeType) {
11566
- NodeType[NodeType["Document"] = 0] = "Document";
11567
- NodeType[NodeType["DocumentType"] = 1] = "DocumentType";
11568
- NodeType[NodeType["Element"] = 2] = "Element";
11569
- NodeType[NodeType["Text"] = 3] = "Text";
11570
- NodeType[NodeType["CDATA"] = 4] = "CDATA";
11571
- NodeType[NodeType["Comment"] = 5] = "Comment";
11572
- })(NodeType || (NodeType = {}));
11573
-
11574
- /**
11575
- * Converts a timestamp to ms, if it was in s, or keeps it as ms.
11576
- */
11577
- function timestampToMs(timestamp) {
11578
- const isMs = timestamp > 9999999999;
11579
- return isMs ? timestamp : timestamp * 1000;
11580
- }
11581
-
11582
- /**
11583
- * Add an event to the event buffer.
11584
- * `isCheckout` is true if this is either the very first event, or an event triggered by `checkoutEveryNms`.
11585
- */
11586
- async function addEvent(
11587
- replay,
11588
- event,
11589
- isCheckout,
11590
- ) {
11591
- if (!replay.eventBuffer) {
11592
- // This implies that `_isEnabled` is false
11593
- return null;
11594
- }
11595
-
11596
- if (replay.isPaused()) {
11597
- // Do not add to event buffer when recording is paused
11598
- return null;
11599
- }
11600
-
11601
- const timestampInMs = timestampToMs(event.timestamp);
11602
-
11603
- // Throw out events that happen more than 5 minutes ago. This can happen if
11604
- // page has been left open and idle for a long period of time and user
11605
- // comes back to trigger a new session. The performance entries rely on
11606
- // `performance.timeOrigin`, which is when the page first opened.
11607
- if (timestampInMs + replay.timeouts.sessionIdlePause < Date.now()) {
11608
- return null;
11609
- }
11610
-
11611
- try {
11612
- if (isCheckout) {
11613
- replay.eventBuffer.clear();
11614
- }
11615
-
11616
- return await replay.eventBuffer.addEvent(event);
11617
- } catch (error) {
11618
- (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.error(error);
11619
- await replay.stop('addEvent');
11620
-
11621
- const client = getCurrentHub().getClient();
11622
-
11623
- if (client) {
11624
- client.recordDroppedEvent('internal_sdk_error', 'replay');
11625
- }
11626
- }
11627
- }
11628
-
11629
- /**
11630
- * Add a breadcrumb event to replay.
11631
- */
11632
- function addBreadcrumbEvent(replay, breadcrumb) {
11633
- if (breadcrumb.category === 'sentry.transaction') {
11634
- return;
11635
- }
11636
-
11637
- if (['ui.click', 'ui.input'].includes(breadcrumb.category )) {
11638
- replay.triggerUserActivity();
11639
- } else {
11640
- replay.checkAndHandleExpiredSession();
11641
- }
11642
-
11643
- replay.addUpdate(() => {
11644
- void addEvent(replay, {
11645
- type: EventType$1.Custom,
11646
- // TODO: We were converting from ms to seconds for breadcrumbs, spans,
11647
- // but maybe we should just keep them as milliseconds
11648
- timestamp: (breadcrumb.timestamp || 0) * 1000,
11649
- data: {
11650
- tag: 'breadcrumb',
11651
- // normalize to max. 10 depth and 1_000 properties per object
11652
- payload: normalize(breadcrumb, 10, 1000),
11653
- },
11654
- });
11655
-
11656
- // Do not flush after console log messages
11657
- return breadcrumb.category === 'console';
11658
- });
11659
- }
11660
-
11661
- /**
11662
- * Detect a slow click on a button/a tag,
11663
- * and potentially create a corresponding breadcrumb.
11664
- */
11665
- function detectSlowClick(
11666
- replay,
11667
- config,
11668
- clickBreadcrumb,
11669
- node,
11670
- ) {
11671
- if (ignoreElement(node, config)) {
11672
- return;
11673
- }
11674
-
11675
- /*
11676
- We consider a slow click a click on a button/a, which does not trigger one of:
11677
- - DOM mutation
11678
- - Scroll (within 100ms)
11679
- Within the given threshold time.
11680
- After time timeout time, we stop listening and mark it as a slow click anyhow.
11681
- */
11682
-
11683
- let cleanup = () => {
11684
- // replaced further down
11685
- };
11686
-
11687
- // After timeout time, def. consider this a slow click, and stop watching for mutations
11688
- const timeout = setTimeout(() => {
11689
- handleSlowClick(replay, clickBreadcrumb, config.timeout, 'timeout');
11690
- cleanup();
11691
- }, config.timeout);
11692
-
11693
- const mutationHandler = () => {
11694
- maybeHandleSlowClick(replay, clickBreadcrumb, config.threshold, config.timeout, 'mutation');
11695
- cleanup();
11696
- };
11697
-
11698
- const scrollHandler = () => {
11699
- maybeHandleSlowClick(replay, clickBreadcrumb, config.scrollTimeout, config.timeout, 'scroll');
11700
- cleanup();
11701
- };
11702
-
11703
- const obs = new MutationObserver(mutationHandler);
11704
-
11705
- obs.observe(WINDOW.document.documentElement, {
11706
- attributes: true,
11707
- characterData: true,
11708
- childList: true,
11709
- subtree: true,
11710
- });
11711
-
11712
- WINDOW.addEventListener('scroll', scrollHandler);
11713
-
11714
- // Stop listening to scroll timeouts early
11715
- const scrollTimeout = setTimeout(() => {
11716
- WINDOW.removeEventListener('scroll', scrollHandler);
11717
- }, config.scrollTimeout);
11718
-
11719
- cleanup = () => {
11720
- clearTimeout(timeout);
11721
- clearTimeout(scrollTimeout);
11722
- obs.disconnect();
11723
- WINDOW.removeEventListener('scroll', scrollHandler);
11724
- };
11725
- }
11726
-
11727
- function maybeHandleSlowClick(
11728
- replay,
11729
- clickBreadcrumb,
11730
- threshold,
11731
- timeout,
11732
- endReason,
11733
- ) {
11734
- const now = Date.now();
11735
- const timeAfterClickMs = now - clickBreadcrumb.timestamp * 1000;
11736
-
11737
- if (timeAfterClickMs > threshold) {
11738
- handleSlowClick(replay, clickBreadcrumb, Math.min(timeAfterClickMs, timeout), endReason);
11739
- return true;
11740
- }
11741
-
11742
- return false;
11743
- }
11744
-
11745
- function handleSlowClick(
11746
- replay,
11747
- clickBreadcrumb,
11748
- timeAfterClickMs,
11749
- endReason,
11750
- ) {
11751
- const breadcrumb = {
11752
- message: clickBreadcrumb.message,
11753
- timestamp: clickBreadcrumb.timestamp,
11754
- category: 'ui.slowClickDetected',
11755
- data: {
11756
- ...clickBreadcrumb.data,
11757
- url: WINDOW.location.href,
11758
- // TODO FN: add parametrized route, when possible
11759
- timeAfterClickMs,
11760
- endReason,
11761
- },
11762
- };
11763
-
11764
- addBreadcrumbEvent(replay, breadcrumb);
11765
- }
11766
-
11767
- const SLOW_CLICK_IGNORE_TAGS = ['SELECT', 'OPTION'];
11768
-
11769
- function ignoreElement(node, config) {
11770
- // If <input> tag, we only want to consider input[type='submit'] & input[type='button']
11771
- if (node.tagName === 'INPUT' && !['submit', 'button'].includes(node.getAttribute('type') || '')) {
11772
- return true;
11773
- }
11774
-
11775
- if (SLOW_CLICK_IGNORE_TAGS.includes(node.tagName)) {
11776
- return true;
11777
- }
11778
-
11779
- // If <a> tag, detect special variants that may not lead to an action
11780
- // If target !== _self, we may open the link somewhere else, which would lead to no action
11781
- // Also, when downloading a file, we may not leave the page, but still not trigger an action
11782
- if (
11783
- node.tagName === 'A' &&
11784
- (node.hasAttribute('download') || (node.hasAttribute('target') && node.getAttribute('target') !== '_self'))
11785
- ) {
11786
- return true;
11787
- }
11788
-
11789
- if (config.ignoreSelector && node.matches(config.ignoreSelector)) {
11790
- return true;
11791
- }
11792
-
11793
- return false;
11794
- }
11795
-
11796
- // Note that these are the serialized attributes and not attributes directly on
11797
- // the DOM Node. Attributes we are interested in:
11798
- const ATTRIBUTES_TO_RECORD = new Set([
11799
- 'id',
11800
- 'class',
11801
- 'aria-label',
11802
- 'role',
11803
- 'name',
11804
- 'alt',
11805
- 'title',
11806
- 'data-test-id',
11807
- 'data-testid',
11808
- ]);
11809
-
11810
- /**
11811
- * Inclusion list of attributes that we want to record from the DOM element
11812
- */
11813
- function getAttributesToRecord(attributes) {
11814
- const obj = {};
11815
- for (const key in attributes) {
11816
- if (ATTRIBUTES_TO_RECORD.has(key)) {
11817
- let normalizedKey = key;
11818
-
11819
- if (key === 'data-testid' || key === 'data-test-id') {
11820
- normalizedKey = 'testId';
11821
- }
11822
-
11823
- obj[normalizedKey] = attributes[key];
11824
- }
11825
- }
11826
-
11827
- return obj;
11828
- }
11829
-
11830
- const handleDomListener = (
11831
- replay,
11832
- ) => {
11833
- const slowClickExperiment = replay.getOptions()._experiments.slowClicks;
11834
-
11835
- const slowClickConfig = slowClickExperiment
11836
- ? {
11837
- threshold: slowClickExperiment.threshold,
11838
- timeout: slowClickExperiment.timeout,
11839
- scrollTimeout: slowClickExperiment.scrollTimeout,
11840
- ignoreSelector: slowClickExperiment.ignoreSelectors ? slowClickExperiment.ignoreSelectors.join(',') : '',
11841
- }
11842
- : undefined;
11843
-
11844
- return (handlerData) => {
11845
- if (!replay.isEnabled()) {
11846
- return;
11847
- }
11848
-
11849
- const result = handleDom(handlerData);
11850
-
11851
- if (!result) {
11852
- return;
11853
- }
11854
-
11855
- const isClick = handlerData.name === 'click';
11856
- const event = isClick && (handlerData.event );
11857
- // Ignore clicks if ctrl/alt/meta keys are held down as they alter behavior of clicks (e.g. open in new tab)
11858
- if (isClick && slowClickConfig && event && !event.altKey && !event.metaKey && !event.ctrlKey) {
11859
- detectSlowClick(
11860
- replay,
11861
- slowClickConfig,
11862
- result ,
11863
- getClickTargetNode(handlerData.event) ,
11864
- );
11865
- }
11866
-
11867
- addBreadcrumbEvent(replay, result);
11868
- };
11869
- };
11870
-
11871
- /** Get the base DOM breadcrumb. */
11872
- function getBaseDomBreadcrumb(target, message) {
11873
- // `__sn` property is the serialized node created by rrweb
11874
- const serializedNode = target && isRrwebNode(target) && target.__sn.type === NodeType.Element ? target.__sn : null;
11875
-
11876
- return {
11877
- message,
11878
- data: serializedNode
11879
- ? {
11880
- nodeId: serializedNode.id,
11881
- node: {
11882
- id: serializedNode.id,
11883
- tagName: serializedNode.tagName,
11884
- textContent: target
11885
- ? Array.from(target.childNodes)
11886
- .map(
11887
- (node) => '__sn' in node && node.__sn.type === NodeType.Text && node.__sn.textContent,
11888
- )
11889
- .filter(Boolean) // filter out empty values
11890
- .map(text => (text ).trim())
11891
- .join('')
11892
- : '',
11893
- attributes: getAttributesToRecord(serializedNode.attributes),
11894
- },
11895
- }
11896
- : {},
11897
- };
11898
- }
11899
-
11900
- /**
11901
- * An event handler to react to DOM events.
11902
- * Exported for tests.
11903
- */
11904
- function handleDom(handlerData) {
11905
- const { target, message } = getDomTarget(handlerData);
11906
-
11907
- return createBreadcrumb({
11908
- category: `ui.${handlerData.name}`,
11909
- ...getBaseDomBreadcrumb(target, message),
11910
- });
11911
- }
11912
-
11913
- function getDomTarget(handlerData) {
11914
- const isClick = handlerData.name === 'click';
11915
-
11916
- let message;
11917
- let target = null;
11918
-
11919
- // Accessing event.target can throw (see getsentry/raven-js#838, #768)
11920
- try {
11921
- target = isClick ? getClickTargetNode(handlerData.event) : getTargetNode(handlerData.event);
11922
- message = htmlTreeAsString(target, { maxStringLength: 200 }) || '<unknown>';
11923
- } catch (e) {
11924
- message = '<unknown>';
11925
- }
11926
-
11927
- return { target, message };
11928
- }
11929
-
11930
- function isRrwebNode(node) {
11931
- return '__sn' in node;
11932
- }
11933
-
11934
- function getTargetNode(event) {
11935
- if (isEventWithTarget(event)) {
11936
- return event.target ;
11937
- }
11938
-
11939
- return event;
11940
- }
11941
-
11942
- const INTERACTIVE_SELECTOR = 'button,a';
11943
-
11944
- // For clicks, we check if the target is inside of a button or link
11945
- // If so, we use this as the target instead
11946
- // This is useful because if you click on the image in <button><img></button>,
11947
- // The target will be the image, not the button, which we don't want here
11948
- function getClickTargetNode(event) {
11949
- const target = getTargetNode(event);
11950
-
11951
- if (!target || !(target instanceof Element)) {
11952
- return target;
11953
- }
11954
-
11955
- const closestInteractive = target.closest(INTERACTIVE_SELECTOR);
11956
- return closestInteractive || target;
11957
- }
11958
-
11959
- function isEventWithTarget(event) {
11960
- return typeof event === 'object' && !!event && 'target' in event;
11961
- }
11962
-
11963
- /** Handle keyboard events & create breadcrumbs. */
11964
- function handleKeyboardEvent(replay, event) {
11965
- if (!replay.isEnabled()) {
11966
- return;
11967
- }
11968
-
11969
- replay.triggerUserActivity();
11970
-
11971
- const breadcrumb = getKeyboardBreadcrumb(event);
11972
-
11973
- if (!breadcrumb) {
11974
- return;
11975
- }
11976
-
11977
- addBreadcrumbEvent(replay, breadcrumb);
11978
- }
11979
-
11980
- /** exported only for tests */
11981
- function getKeyboardBreadcrumb(event) {
11982
- const { metaKey, shiftKey, ctrlKey, altKey, key, target } = event;
11983
-
11984
- // never capture for input fields
11985
- if (!target || isInputElement(target )) {
11986
- return null;
11987
- }
11988
-
11989
- // Note: We do not consider shift here, as that means "uppercase"
11990
- const hasModifierKey = metaKey || ctrlKey || altKey;
11991
- const isCharacterKey = key.length === 1; // other keys like Escape, Tab, etc have a longer length
11992
-
11993
- // Do not capture breadcrumb if only a word key is pressed
11994
- // This could leak e.g. user input
11995
- if (!hasModifierKey && isCharacterKey) {
11996
- return null;
11997
- }
11998
-
11999
- const message = htmlTreeAsString(target, { maxStringLength: 200 }) || '<unknown>';
12000
- const baseBreadcrumb = getBaseDomBreadcrumb(target , message);
12001
-
12002
- return createBreadcrumb({
12003
- category: 'ui.keyDown',
12004
- message,
12005
- data: {
12006
- ...baseBreadcrumb.data,
12007
- metaKey,
12008
- shiftKey,
12009
- ctrlKey,
12010
- altKey,
12011
- key,
12012
- },
12013
- });
12014
- }
12015
-
12016
- function isInputElement(target) {
12017
- return target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable;
12018
- }
12019
-
12020
11320
  const NAVIGATION_ENTRY_KEYS = [
12021
11321
  'name',
12022
11322
  'type',
@@ -12170,19 +11470,20 @@ class EventBufferArray {
12170
11470
  return this.events.length > 0;
12171
11471
  }
12172
11472
 
12173
- /** @inheritdoc */
12174
- get type() {
12175
- return 'sync';
12176
- }
12177
-
12178
11473
  /** @inheritdoc */
12179
11474
  destroy() {
12180
11475
  this.events = [];
12181
11476
  }
12182
11477
 
12183
11478
  /** @inheritdoc */
12184
- async addEvent(event) {
11479
+ async addEvent(event, isCheckout) {
11480
+ if (isCheckout) {
11481
+ this.events = [event];
11482
+ return;
11483
+ }
11484
+
12185
11485
  this.events.push(event);
11486
+ return;
12186
11487
  }
12187
11488
 
12188
11489
  /** @inheritdoc */
@@ -12196,22 +11497,6 @@ class EventBufferArray {
12196
11497
  resolve(JSON.stringify(eventsRet));
12197
11498
  });
12198
11499
  }
12199
-
12200
- /** @inheritdoc */
12201
- clear() {
12202
- this.events = [];
12203
- }
12204
-
12205
- /** @inheritdoc */
12206
- getEarliestTimestamp() {
12207
- const timestamp = this.events.map(event => event.timestamp).sort()[0];
12208
-
12209
- if (!timestamp) {
12210
- return null;
12211
- }
12212
-
12213
- return timestampToMs(timestamp);
12214
- }
12215
11500
  }
12216
11501
 
12217
11502
  /**
@@ -12319,20 +11604,11 @@ class WorkerHandler {
12319
11604
  * Exported only for testing.
12320
11605
  */
12321
11606
  class EventBufferCompressionWorker {
11607
+ /** @inheritdoc */
12322
11608
 
12323
11609
  constructor(worker) {
12324
11610
  this._worker = new WorkerHandler(worker);
12325
- this._earliestTimestamp = null;
12326
- }
12327
-
12328
- /** @inheritdoc */
12329
- get hasEvents() {
12330
- return !!this._earliestTimestamp;
12331
- }
12332
-
12333
- /** @inheritdoc */
12334
- get type() {
12335
- return 'worker';
11611
+ this.hasEvents = false;
12336
11612
  }
12337
11613
 
12338
11614
  /**
@@ -12355,10 +11631,13 @@ class EventBufferCompressionWorker {
12355
11631
  *
12356
11632
  * Returns true if event was successfuly received and processed by worker.
12357
11633
  */
12358
- addEvent(event) {
12359
- const timestamp = timestampToMs(event.timestamp);
12360
- if (!this._earliestTimestamp || timestamp < this._earliestTimestamp) {
12361
- this._earliestTimestamp = timestamp;
11634
+ async addEvent(event, isCheckout) {
11635
+ this.hasEvents = true;
11636
+
11637
+ if (isCheckout) {
11638
+ // This event is a checkout, make sure worker buffer is cleared before
11639
+ // proceeding.
11640
+ await this._clear();
12362
11641
  }
12363
11642
 
12364
11643
  return this._sendEventToWorker(event);
@@ -12371,18 +11650,6 @@ class EventBufferCompressionWorker {
12371
11650
  return this._finishRequest();
12372
11651
  }
12373
11652
 
12374
- /** @inheritdoc */
12375
- clear() {
12376
- this._earliestTimestamp = null;
12377
- // We do not wait on this, as we assume the order of messages is consistent for the worker
12378
- void this._worker.postMessage('clear');
12379
- }
12380
-
12381
- /** @inheritdoc */
12382
- getEarliestTimestamp() {
12383
- return this._earliestTimestamp;
12384
- }
12385
-
12386
11653
  /**
12387
11654
  * Send the event to the worker.
12388
11655
  */
@@ -12396,10 +11663,15 @@ class EventBufferCompressionWorker {
12396
11663
  async _finishRequest() {
12397
11664
  const response = await this._worker.postMessage('finish');
12398
11665
 
12399
- this._earliestTimestamp = null;
11666
+ this.hasEvents = false;
12400
11667
 
12401
11668
  return response;
12402
11669
  }
11670
+
11671
+ /** Clear any pending events from the worker. */
11672
+ _clear() {
11673
+ return this._worker.postMessage('clear');
11674
+ }
12403
11675
  }
12404
11676
 
12405
11677
  /**
@@ -12417,11 +11689,6 @@ class EventBufferProxy {
12417
11689
  this._ensureWorkerIsLoadedPromise = this._ensureWorkerIsLoaded();
12418
11690
  }
12419
11691
 
12420
- /** @inheritdoc */
12421
- get type() {
12422
- return this._used.type;
12423
- }
12424
-
12425
11692
  /** @inheritDoc */
12426
11693
  get hasEvents() {
12427
11694
  return this._used.hasEvents;
@@ -12433,23 +11700,13 @@ class EventBufferProxy {
12433
11700
  this._compression.destroy();
12434
11701
  }
12435
11702
 
12436
- /** @inheritdoc */
12437
- clear() {
12438
- return this._used.clear();
12439
- }
12440
-
12441
- /** @inheritdoc */
12442
- getEarliestTimestamp() {
12443
- return this._used.getEarliestTimestamp();
12444
- }
12445
-
12446
11703
  /**
12447
11704
  * Add an event to the event buffer.
12448
11705
  *
12449
11706
  * Returns true if event was successfully added.
12450
11707
  */
12451
- addEvent(event) {
12452
- return this._used.addEvent(event);
11708
+ addEvent(event, isCheckout) {
11709
+ return this._used.addEvent(event, isCheckout);
12453
11710
  }
12454
11711
 
12455
11712
  /** @inheritDoc */
@@ -12524,31 +11781,6 @@ function createEventBuffer({ useCompression }) {
12524
11781
  return new EventBufferArray();
12525
11782
  }
12526
11783
 
12527
- /**
12528
- * Removes the session from Session Storage and unsets session in replay instance
12529
- */
12530
- function clearSession(replay) {
12531
- deleteSession();
12532
- replay.session = undefined;
12533
- }
12534
-
12535
- /**
12536
- * Deletes a session from storage
12537
- */
12538
- function deleteSession() {
12539
- const hasSessionStorage = 'sessionStorage' in WINDOW;
12540
-
12541
- if (!hasSessionStorage) {
12542
- return;
12543
- }
12544
-
12545
- try {
12546
- WINDOW.sessionStorage.removeItem(REPLAY_SESSION_KEY);
12547
- } catch (e) {
12548
- // Ignore potential SecurityError exceptions
12549
- }
12550
- }
12551
-
12552
11784
  /**
12553
11785
  * Given an initial timestamp and an expiry duration, checks to see if current
12554
11786
  * time should be considered as expired.
@@ -12579,26 +11811,11 @@ function isSessionExpired(session, timeouts, targetTime = +new Date()) {
12579
11811
  // First, check that maximum session length has not been exceeded
12580
11812
  isExpired(session.started, timeouts.maxSessionLife, targetTime) ||
12581
11813
  // check that the idle timeout has not been exceeded (i.e. user has
12582
- // performed an action within the last `sessionIdleExpire` ms)
12583
- isExpired(session.lastActivity, timeouts.sessionIdleExpire, targetTime)
11814
+ // performed an action within the last `idleTimeout` ms)
11815
+ isExpired(session.lastActivity, timeouts.sessionIdle, targetTime)
12584
11816
  );
12585
11817
  }
12586
11818
 
12587
- /**
12588
- * Given a sample rate, returns true if replay should be sampled.
12589
- *
12590
- * 1.0 = 100% sampling
12591
- * 0.0 = 0% sampling
12592
- */
12593
- function isSampled(sampleRate) {
12594
- if (sampleRate === undefined) {
12595
- return false;
12596
- }
12597
-
12598
- // Math.random() returns a number in range of 0 to 1 (inclusive of 0, but not 1)
12599
- return Math.random() < sampleRate;
12600
- }
12601
-
12602
11819
  /**
12603
11820
  * Save a session to session storage.
12604
11821
  */
@@ -12615,6 +11832,21 @@ function saveSession(session) {
12615
11832
  }
12616
11833
  }
12617
11834
 
11835
+ /**
11836
+ * Given a sample rate, returns true if replay should be sampled.
11837
+ *
11838
+ * 1.0 = 100% sampling
11839
+ * 0.0 = 0% sampling
11840
+ */
11841
+ function isSampled(sampleRate) {
11842
+ if (sampleRate === undefined) {
11843
+ return false;
11844
+ }
11845
+
11846
+ // Math.random() returns a number in range of 0 to 1 (inclusive of 0, but not 1)
11847
+ return Math.random() < sampleRate;
11848
+ }
11849
+
12618
11850
  /**
12619
11851
  * Get a session with defaults & applied sampling.
12620
11852
  */
@@ -12633,15 +11865,14 @@ function makeSession(session) {
12633
11865
  lastActivity,
12634
11866
  segmentId,
12635
11867
  sampled,
12636
- shouldRefresh: true,
12637
11868
  };
12638
11869
  }
12639
11870
 
12640
11871
  /**
12641
11872
  * Get the sampled status for a session based on sample rates & current sampled status.
12642
11873
  */
12643
- function getSessionSampleType(sessionSampleRate, allowBuffering) {
12644
- return isSampled(sessionSampleRate) ? 'session' : allowBuffering ? 'buffer' : false;
11874
+ function getSessionSampleType(sessionSampleRate, errorSampleRate) {
11875
+ return isSampled(sessionSampleRate) ? 'session' : isSampled(errorSampleRate) ? 'error' : false;
12645
11876
  }
12646
11877
 
12647
11878
  /**
@@ -12649,8 +11880,8 @@ function getSessionSampleType(sessionSampleRate, allowBuffering) {
12649
11880
  * that all replays will be saved to as attachments. Currently, we only expect
12650
11881
  * one of these Sentry events per "replay session".
12651
11882
  */
12652
- function createSession({ sessionSampleRate, allowBuffering, stickySession = false }) {
12653
- const sampled = getSessionSampleType(sessionSampleRate, allowBuffering);
11883
+ function createSession({ sessionSampleRate, errorSampleRate, stickySession = false }) {
11884
+ const sampled = getSessionSampleType(sessionSampleRate, errorSampleRate);
12654
11885
  const session = makeSession({
12655
11886
  sampled,
12656
11887
  });
@@ -12698,7 +11929,7 @@ function getSession({
12698
11929
  currentSession,
12699
11930
  stickySession,
12700
11931
  sessionSampleRate,
12701
- allowBuffering,
11932
+ errorSampleRate,
12702
11933
  }) {
12703
11934
  // If session exists and is passed, use it instead of always hitting session storage
12704
11935
  const session = currentSession || (stickySession && fetchSession());
@@ -12711,9 +11942,8 @@ function getSession({
12711
11942
 
12712
11943
  if (!isExpired) {
12713
11944
  return { type: 'saved', session };
12714
- } else if (!session.shouldRefresh) {
12715
- // In this case, stop
12716
- // This is the case if we have an error session that is completed (=triggered an error)
11945
+ } else if (session.sampled === 'error') {
11946
+ // Error samples should not be re-created when expired, but instead we stop when the replay is done
12717
11947
  const discardedSession = makeSession({ sampled: false });
12718
11948
  return { type: 'new', session: discardedSession };
12719
11949
  } else {
@@ -12725,12 +11955,65 @@ function getSession({
12725
11955
  const newSession = createSession({
12726
11956
  stickySession,
12727
11957
  sessionSampleRate,
12728
- allowBuffering,
11958
+ errorSampleRate,
12729
11959
  });
12730
11960
 
12731
11961
  return { type: 'new', session: newSession };
12732
11962
  }
12733
11963
 
11964
+ /**
11965
+ * Add an event to the event buffer.
11966
+ * `isCheckout` is true if this is either the very first event, or an event triggered by `checkoutEveryNms`.
11967
+ */
11968
+ async function addEvent(
11969
+ replay,
11970
+ event,
11971
+ isCheckout,
11972
+ ) {
11973
+ if (!replay.eventBuffer) {
11974
+ // This implies that `_isEnabled` is false
11975
+ return null;
11976
+ }
11977
+
11978
+ if (replay.isPaused()) {
11979
+ // Do not add to event buffer when recording is paused
11980
+ return null;
11981
+ }
11982
+
11983
+ // TODO: sadness -- we will want to normalize timestamps to be in ms -
11984
+ // requires coordination with frontend
11985
+ const isMs = event.timestamp > 9999999999;
11986
+ const timestampInMs = isMs ? event.timestamp : event.timestamp * 1000;
11987
+
11988
+ // Throw out events that happen more than 5 minutes ago. This can happen if
11989
+ // page has been left open and idle for a long period of time and user
11990
+ // comes back to trigger a new session. The performance entries rely on
11991
+ // `performance.timeOrigin`, which is when the page first opened.
11992
+ if (timestampInMs + replay.timeouts.sessionIdle < Date.now()) {
11993
+ return null;
11994
+ }
11995
+
11996
+ // Only record earliest event if a new session was created, otherwise it
11997
+ // shouldn't be relevant
11998
+ const earliestEvent = replay.getContext().earliestEvent;
11999
+ if (replay.session && replay.session.segmentId === 0 && (!earliestEvent || timestampInMs < earliestEvent)) {
12000
+ replay.getContext().earliestEvent = timestampInMs;
12001
+ }
12002
+
12003
+ try {
12004
+ return await replay.eventBuffer.addEvent(event, isCheckout);
12005
+ } catch (error) {
12006
+ (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.error(error);
12007
+ replay.stop('addEvent');
12008
+
12009
+ const client = getCurrentHub().getClient();
12010
+
12011
+ if (client) {
12012
+ client.recordDroppedEvent('internal_sdk_error', 'replay');
12013
+ }
12014
+ }
12015
+ }
12016
+
12734
12017
  /** If the event is an error event */
12735
12018
  function isErrorEvent(event) {
12736
12019
  return !event.type;
@@ -12780,21 +12063,31 @@ function handleAfterSendEvent(replay) {
12780
12063
  return;
12781
12064
  }
12782
12065
 
12783
- // Add error to list of errorIds of replay. This is ok to do even if not
12784
- // sampled because context will get reset at next checkout.
12785
- // XXX: There is also a race condition where it's possible to capture an
12786
- // error to Sentry before Replay SDK has loaded, but response returns after
12787
- // it was loaded, and this gets called.
12066
+ // Add error to list of errorIds of replay
12788
12067
  if (event.event_id) {
12789
12068
  replay.getContext().errorIds.add(event.event_id);
12790
12069
  }
12791
12070
 
12792
- // If error event is tagged with replay id it means it was sampled (when in buffer mode)
12071
+ // Trigger error recording
12793
12072
  // Need to be very careful that this does not cause an infinite loop
12794
- if (replay.recordingMode === 'buffer' && event.tags && event.tags.replayId) {
12795
- setTimeout(() => {
12796
- // Capture current event buffer as new replay
12797
- void replay.sendBufferedReplayOrFlush();
12073
+ if (
12074
+ replay.recordingMode === 'error' &&
12075
+ event.exception &&
12076
+ event.message !== UNABLE_TO_SEND_REPLAY // ignore this error because otherwise we could loop indefinitely with trying to capture replay and failing
12077
+ ) {
12078
+ setTimeout(async () => {
12079
+ // Allow flush to complete before resuming as a session recording, otherwise
12080
+ // the checkout from `startRecording` may be included in the payload.
12081
+ // Prefer to keep the error replay as a separate (and smaller) segment
12082
+ // than the session replay.
12083
+ await replay.flushImmediate();
12084
+
12085
+ if (replay.stopRecording()) {
12086
+ // Reset all "capture on error" configuration before
12087
+ // starting a new recording
12088
+ replay.recordingMode = 'session';
12089
+ replay.startRecording();
12090
+ }
12798
12091
  });
12799
12092
  }
12800
12093
  };
@@ -12816,6 +12109,166 @@ function isBaseTransportSend() {
12816
12109
  );
12817
12110
  }
12818
12111
 
12112
+ var NodeType;
12113
+ (function (NodeType) {
12114
+ NodeType[NodeType["Document"] = 0] = "Document";
12115
+ NodeType[NodeType["DocumentType"] = 1] = "DocumentType";
12116
+ NodeType[NodeType["Element"] = 2] = "Element";
12117
+ NodeType[NodeType["Text"] = 3] = "Text";
12118
+ NodeType[NodeType["CDATA"] = 4] = "CDATA";
12119
+ NodeType[NodeType["Comment"] = 5] = "Comment";
12120
+ })(NodeType || (NodeType = {}));
12121
+
12122
+ /**
12123
+ * Create a breadcrumb for a replay.
12124
+ */
12125
+ function createBreadcrumb(
12126
+ breadcrumb,
12127
+ ) {
12128
+ return {
12129
+ timestamp: Date.now() / 1000,
12130
+ type: 'default',
12131
+ ...breadcrumb,
12132
+ };
12133
+ }
12134
+
12135
+ /**
12136
+ * Add a breadcrumb event to replay.
12137
+ */
12138
+ function addBreadcrumbEvent(replay, breadcrumb) {
12139
+ if (breadcrumb.category === 'sentry.transaction') {
12140
+ return;
12141
+ }
12142
+
12143
+ if (['ui.click', 'ui.input'].includes(breadcrumb.category )) {
12144
+ replay.triggerUserActivity();
12145
+ } else {
12146
+ replay.checkAndHandleExpiredSession();
12147
+ }
12148
+
12149
+ replay.addUpdate(() => {
12150
+ void addEvent(replay, {
12151
+ type: EventType.Custom,
12152
+ // TODO: We were converting from ms to seconds for breadcrumbs, spans,
12153
+ // but maybe we should just keep them as milliseconds
12154
+ timestamp: (breadcrumb.timestamp || 0) * 1000,
12155
+ data: {
12156
+ tag: 'breadcrumb',
12157
+ payload: breadcrumb,
12158
+ },
12159
+ });
12160
+
12161
+ // Do not flush after console log messages
12162
+ return breadcrumb.category === 'console';
12163
+ });
12164
+ }
12165
+
12166
+ // Note that these are the serialized attributes and not attributes directly on
12167
+ // the DOM Node. Attributes we are interested in:
12168
+ const ATTRIBUTES_TO_RECORD = new Set([
12169
+ 'id',
12170
+ 'class',
12171
+ 'aria-label',
12172
+ 'role',
12173
+ 'name',
12174
+ 'alt',
12175
+ 'title',
12176
+ 'data-test-id',
12177
+ 'data-testid',
12178
+ ]);
12179
+
12180
+ /**
12181
+ * Inclusion list of attributes that we want to record from the DOM element
12182
+ */
12183
+ function getAttributesToRecord(attributes) {
12184
+ const obj = {};
12185
+ for (const key in attributes) {
12186
+ if (ATTRIBUTES_TO_RECORD.has(key)) {
12187
+ let normalizedKey = key;
12188
+
12189
+ if (key === 'data-testid' || key === 'data-test-id') {
12190
+ normalizedKey = 'testId';
12191
+ }
12192
+
12193
+ obj[normalizedKey] = attributes[key];
12194
+ }
12195
+ }
12196
+
12197
+ return obj;
12198
+ }
12199
+
12200
+ const handleDomListener =
12201
+ (replay) =>
12202
+ (handlerData) => {
12203
+ if (!replay.isEnabled()) {
12204
+ return;
12205
+ }
12206
+
12207
+ const result = handleDom(handlerData);
12208
+
12209
+ if (!result) {
12210
+ return;
12211
+ }
12212
+
12213
+ addBreadcrumbEvent(replay, result);
12214
+ };
12215
+
12216
+ /**
12217
+ * An event handler to react to DOM events.
12218
+ */
12219
+ function handleDom(handlerData) {
12220
+ let target;
12221
+ let targetNode;
12222
+
12223
+ // Accessing event.target can throw (see getsentry/raven-js#838, #768)
12224
+ try {
12225
+ targetNode = getTargetNode(handlerData);
12226
+ target = htmlTreeAsString(targetNode);
12227
+ } catch (e) {
12228
+ target = '<unknown>';
12229
+ }
12230
+
12231
+ // `__sn` property is the serialized node created by rrweb
12232
+ const serializedNode =
12233
+ targetNode && '__sn' in targetNode && targetNode.__sn.type === NodeType.Element ? targetNode.__sn : null;
12234
+
12235
+ return createBreadcrumb({
12236
+ category: `ui.${handlerData.name}`,
12237
+ message: target,
12238
+ data: serializedNode
12239
+ ? {
12240
+ nodeId: serializedNode.id,
12241
+ node: {
12242
+ id: serializedNode.id,
12243
+ tagName: serializedNode.tagName,
12244
+ textContent: targetNode
12245
+ ? Array.from(targetNode.childNodes)
12246
+ .map(
12247
+ (node) => '__sn' in node && node.__sn.type === NodeType.Text && node.__sn.textContent,
12248
+ )
12249
+ .filter(Boolean) // filter out empty values
12250
+ .map(text => (text ).trim())
12251
+ .join('')
12252
+ : '',
12253
+ attributes: getAttributesToRecord(serializedNode.attributes),
12254
+ },
12255
+ }
12256
+ : {},
12257
+ });
12258
+ }
12259
+
12260
+ function getTargetNode(handlerData) {
12261
+ if (isEventWithTarget(handlerData.event)) {
12262
+ return handlerData.event.target;
12263
+ }
12264
+
12265
+ return handlerData.event;
12266
+ }
12267
+
12268
+ function isEventWithTarget(event) {
12269
+ return !!(event ).target;
12270
+ }
12271
+
12819
12272
  /**
12820
12273
  * Returns true if we think the given event is an error originating inside of rrweb.
12821
12274
  */
@@ -12839,30 +12292,6 @@ function isRrwebError(event, hint) {
12839
12292
  });
12840
12293
  }
12841
12294
 
12842
- /**
12843
- * Determine if event should be sampled (only applies in buffer mode).
12844
- * When an event is captured by `hanldleGlobalEvent`, when in buffer mode
12845
- * we determine if we want to sample the error or not.
12846
- */
12847
- function shouldSampleForBufferEvent(replay, event) {
12848
- if (replay.recordingMode !== 'buffer') {
12849
- return false;
12850
- }
12851
-
12852
- // ignore this error because otherwise we could loop indefinitely with
12853
- // trying to capture replay and failing
12854
- if (event.message === UNABLE_TO_SEND_REPLAY) {
12855
- return false;
12856
- }
12857
-
12858
- // Require the event to be an error event & to have an exception
12859
- if (!event.exception || event.type) {
12860
- return false;
12861
- }
12862
-
12863
- return isSampled(replay.getOptions().errorSampleRate);
12864
- }
12865
-
12866
12295
  /**
12867
12296
  * Returns a listener to be added to `addGlobalEventProcessor(listener)`.
12868
12297
  */
@@ -12892,16 +12321,8 @@ function handleGlobalEventListener(
12892
12321
  return null;
12893
12322
  }
12894
12323
 
12895
- // When in buffer mode, we decide to sample here.
12896
- // Later, in `handleAfterSendEvent`, if the replayId is set, we know that we sampled
12897
- // And convert the buffer session to a full session
12898
- const isErrorEventSampled = shouldSampleForBufferEvent(replay, event);
12899
-
12900
- // Tag errors if it has been sampled in buffer mode, or if it is session mode
12901
- // Only tag transactions if in session mode
12902
- const shouldTagReplayId = isErrorEventSampled || replay.recordingMode === 'session';
12903
-
12904
- if (shouldTagReplayId) {
12324
+ // Only tag transactions with replayId if not waiting for an error
12325
+ if (isErrorEvent(event) || (isTransactionEvent(event) && replay.recordingMode === 'session')) {
12905
12326
  event.tags = { ...event.tags, replayId: replay.getSessionId() };
12906
12327
  }
12907
12328
 
@@ -12951,7 +12372,7 @@ function createPerformanceSpans(
12951
12372
  ) {
12952
12373
  return entries.map(({ type, start, end, name, data }) =>
12953
12374
  addEvent(replay, {
12954
- type: EventType$1.Custom,
12375
+ type: EventType.Custom,
12955
12376
  timestamp: start,
12956
12377
  data: {
12957
12378
  tag: 'performanceSpan',
@@ -13100,14 +12521,12 @@ function handleFetchSpanListener(replay) {
13100
12521
  function handleXhr(handlerData) {
13101
12522
  const { startTimestamp, endTimestamp, xhr } = handlerData;
13102
12523
 
13103
- const sentryXhrData = xhr[SENTRY_XHR_DATA_KEY];
13104
-
13105
- if (!startTimestamp || !endTimestamp || !sentryXhrData) {
12524
+ if (!startTimestamp || !endTimestamp || !xhr.__sentry_xhr__) {
13106
12525
  return null;
13107
12526
  }
13108
12527
 
13109
12528
  // This is only used as a fallback, so we know the body sizes are never set here
13110
- const { method, url, status_code: statusCode } = sentryXhrData;
12529
+ const { method, url, status_code: statusCode } = xhr.__sentry_xhr__;
13111
12530
 
13112
12531
  if (url === undefined) {
13113
12532
  return null;
@@ -13140,393 +12559,6 @@ function handleXhrSpanListener(replay) {
13140
12559
  };
13141
12560
  }
13142
12561
 
13143
- const OBJ = 10;
13144
- const OBJ_KEY = 11;
13145
- const OBJ_KEY_STR = 12;
13146
- const OBJ_VAL = 13;
13147
- const OBJ_VAL_STR = 14;
13148
- const OBJ_VAL_COMPLETED = 15;
13149
-
13150
- const ARR = 20;
13151
- const ARR_VAL = 21;
13152
- const ARR_VAL_STR = 22;
13153
- const ARR_VAL_COMPLETED = 23;
13154
-
13155
- const ALLOWED_PRIMITIVES = ['true', 'false', 'null'];
13156
-
13157
- /**
13158
- * Complete an incomplete JSON string.
13159
- * This will ensure that the last element always has a `"~~"` to indicate it was truncated.
13160
- * For example, `[1,2,` will be completed to `[1,2,"~~"]`
13161
- * and `{"aa":"b` will be completed to `{"aa":"b~~"}`
13162
- */
13163
- function completeJson(incompleteJson, stack) {
13164
- if (!stack.length) {
13165
- return incompleteJson;
13166
- }
13167
-
13168
- let json = incompleteJson;
13169
-
13170
- // Most checks are only needed for the last step in the stack
13171
- const lastPos = stack.length - 1;
13172
- const lastStep = stack[lastPos];
13173
-
13174
- json = _fixLastStep(json, lastStep);
13175
-
13176
- // Complete remaining steps - just add closing brackets
13177
- for (let i = lastPos; i >= 0; i--) {
13178
- const step = stack[i];
13179
-
13180
- switch (step) {
13181
- case OBJ:
13182
- json = `${json}}`;
13183
- break;
13184
- case ARR:
13185
- json = `${json}]`;
13186
- break;
13187
- }
13188
- }
13189
-
13190
- return json;
13191
- }
13192
-
13193
- function _fixLastStep(json, lastStep) {
13194
- switch (lastStep) {
13195
- // Object cases
13196
- case OBJ:
13197
- return `${json}"~~":"~~"`;
13198
- case OBJ_KEY:
13199
- return `${json}:"~~"`;
13200
- case OBJ_KEY_STR:
13201
- return `${json}~~":"~~"`;
13202
- case OBJ_VAL:
13203
- return _maybeFixIncompleteObjValue(json);
13204
- case OBJ_VAL_STR:
13205
- return `${json}~~"`;
13206
- case OBJ_VAL_COMPLETED:
13207
- return `${json},"~~":"~~"`;
13208
-
13209
- // Array cases
13210
- case ARR:
13211
- return `${json}"~~"`;
13212
- case ARR_VAL:
13213
- return _maybeFixIncompleteArrValue(json);
13214
- case ARR_VAL_STR:
13215
- return `${json}~~"`;
13216
- case ARR_VAL_COMPLETED:
13217
- return `${json},"~~"`;
13218
- }
13219
-
13220
- return json;
13221
- }
13222
-
13223
- function _maybeFixIncompleteArrValue(json) {
13224
- const pos = _findLastArrayDelimiter(json);
13225
-
13226
- if (pos > -1) {
13227
- const part = json.slice(pos + 1);
13228
-
13229
- if (ALLOWED_PRIMITIVES.includes(part.trim())) {
13230
- return `${json},"~~"`;
13231
- }
13232
-
13233
- // Everything else is replaced with `"~~"`
13234
- return `${json.slice(0, pos + 1)}"~~"`;
13235
- }
13236
-
13237
- // fallback, this shouldn't happen, to be save
13238
- return json;
13239
- }
13240
-
13241
- function _findLastArrayDelimiter(json) {
13242
- for (let i = json.length - 1; i >= 0; i--) {
13243
- const char = json[i];
13244
-
13245
- if (char === ',' || char === '[') {
13246
- return i;
13247
- }
13248
- }
13249
-
13250
- return -1;
13251
- }
13252
-
13253
- function _maybeFixIncompleteObjValue(json) {
13254
- const startPos = json.lastIndexOf(':');
13255
-
13256
- const part = json.slice(startPos + 1);
13257
-
13258
- if (ALLOWED_PRIMITIVES.includes(part.trim())) {
13259
- return `${json},"~~":"~~"`;
13260
- }
13261
-
13262
- // Everything else is replaced with `"~~"`
13263
- // This also means we do not have incomplete numbers, e.g `[1` is replaced with `["~~"]`
13264
- return `${json.slice(0, startPos + 1)}"~~"`;
13265
- }
13266
-
13267
- /**
13268
- * Evaluate an (incomplete) JSON string.
13269
- */
13270
- function evaluateJson(json) {
13271
- const stack = [];
13272
-
13273
- for (let pos = 0; pos < json.length; pos++) {
13274
- _evaluateJsonPos(stack, json, pos);
13275
- }
13276
-
13277
- return stack;
13278
- }
13279
-
13280
- function _evaluateJsonPos(stack, json, pos) {
13281
- const curStep = stack[stack.length - 1];
13282
-
13283
- const char = json[pos];
13284
-
13285
- const whitespaceRegex = /\s/;
13286
-
13287
- if (whitespaceRegex.test(char)) {
13288
- return;
13289
- }
13290
-
13291
- if (char === '"' && !_isEscaped(json, pos)) {
13292
- _handleQuote(stack, curStep);
13293
- return;
13294
- }
13295
-
13296
- switch (char) {
13297
- case '{':
13298
- _handleObj(stack, curStep);
13299
- break;
13300
- case '[':
13301
- _handleArr(stack, curStep);
13302
- break;
13303
- case ':':
13304
- _handleColon(stack, curStep);
13305
- break;
13306
- case ',':
13307
- _handleComma(stack, curStep);
13308
- break;
13309
- case '}':
13310
- _handleObjClose(stack, curStep);
13311
- break;
13312
- case ']':
13313
- _handleArrClose(stack, curStep);
13314
- break;
13315
- }
13316
- }
13317
-
13318
- function _handleQuote(stack, curStep) {
13319
- // End of obj value
13320
- if (curStep === OBJ_VAL_STR) {
13321
- stack.pop();
13322
- stack.push(OBJ_VAL_COMPLETED);
13323
- return;
13324
- }
13325
-
13326
- // End of arr value
13327
- if (curStep === ARR_VAL_STR) {
13328
- stack.pop();
13329
- stack.push(ARR_VAL_COMPLETED);
13330
- return;
13331
- }
13332
-
13333
- // Start of obj value
13334
- if (curStep === OBJ_VAL) {
13335
- stack.push(OBJ_VAL_STR);
13336
- return;
13337
- }
13338
-
13339
- // Start of arr value
13340
- if (curStep === ARR_VAL) {
13341
- stack.push(ARR_VAL_STR);
13342
- return;
13343
- }
13344
-
13345
- // Start of obj key
13346
- if (curStep === OBJ) {
13347
- stack.push(OBJ_KEY_STR);
13348
- return;
13349
- }
13350
-
13351
- // End of obj key
13352
- if (curStep === OBJ_KEY_STR) {
13353
- stack.pop();
13354
- stack.push(OBJ_KEY);
13355
- return;
13356
- }
13357
- }
13358
-
13359
- function _handleObj(stack, curStep) {
13360
- // Initial object
13361
- if (!curStep) {
13362
- stack.push(OBJ);
13363
- return;
13364
- }
13365
-
13366
- // New object as obj value
13367
- if (curStep === OBJ_VAL) {
13368
- stack.push(OBJ);
13369
- return;
13370
- }
13371
-
13372
- // New object as array element
13373
- if (curStep === ARR_VAL) {
13374
- stack.push(OBJ);
13375
- }
13376
-
13377
- // New object as first array element
13378
- if (curStep === ARR) {
13379
- stack.push(OBJ);
13380
- return;
13381
- }
13382
- }
13383
-
13384
- function _handleArr(stack, curStep) {
13385
- // Initial array
13386
- if (!curStep) {
13387
- stack.push(ARR);
13388
- stack.push(ARR_VAL);
13389
- return;
13390
- }
13391
-
13392
- // New array as obj value
13393
- if (curStep === OBJ_VAL) {
13394
- stack.push(ARR);
13395
- stack.push(ARR_VAL);
13396
- return;
13397
- }
13398
-
13399
- // New array as array element
13400
- if (curStep === ARR_VAL) {
13401
- stack.push(ARR);
13402
- stack.push(ARR_VAL);
13403
- }
13404
-
13405
- // New array as first array element
13406
- if (curStep === ARR) {
13407
- stack.push(ARR);
13408
- stack.push(ARR_VAL);
13409
- return;
13410
- }
13411
- }
13412
-
13413
- function _handleColon(stack, curStep) {
13414
- if (curStep === OBJ_KEY) {
13415
- stack.pop();
13416
- stack.push(OBJ_VAL);
13417
- }
13418
- }
13419
-
13420
- function _handleComma(stack, curStep) {
13421
- // Comma after obj value
13422
- if (curStep === OBJ_VAL) {
13423
- stack.pop();
13424
- return;
13425
- }
13426
- if (curStep === OBJ_VAL_COMPLETED) {
13427
- // Pop OBJ_VAL_COMPLETED & OBJ_VAL
13428
- stack.pop();
13429
- stack.pop();
13430
- return;
13431
- }
13432
-
13433
- // Comma after arr value
13434
- if (curStep === ARR_VAL) {
13435
- // do nothing - basically we'd pop ARR_VAL but add it right back
13436
- return;
13437
- }
13438
-
13439
- if (curStep === ARR_VAL_COMPLETED) {
13440
- // Pop ARR_VAL_COMPLETED
13441
- stack.pop();
13442
-
13443
- // basically we'd pop ARR_VAL but add it right back
13444
- return;
13445
- }
13446
- }
13447
-
13448
- function _handleObjClose(stack, curStep) {
13449
- // Empty object {}
13450
- if (curStep === OBJ) {
13451
- stack.pop();
13452
- }
13453
-
13454
- // Object with element
13455
- if (curStep === OBJ_VAL) {
13456
- // Pop OBJ_VAL, OBJ
13457
- stack.pop();
13458
- stack.pop();
13459
- }
13460
-
13461
- // Obj with element
13462
- if (curStep === OBJ_VAL_COMPLETED) {
13463
- // Pop OBJ_VAL_COMPLETED, OBJ_VAL, OBJ
13464
- stack.pop();
13465
- stack.pop();
13466
- stack.pop();
13467
- }
13468
-
13469
- // if was obj value, complete it
13470
- if (stack[stack.length - 1] === OBJ_VAL) {
13471
- stack.push(OBJ_VAL_COMPLETED);
13472
- }
13473
-
13474
- // if was arr value, complete it
13475
- if (stack[stack.length - 1] === ARR_VAL) {
13476
- stack.push(ARR_VAL_COMPLETED);
13477
- }
13478
- }
13479
-
13480
- function _handleArrClose(stack, curStep) {
13481
- // Empty array []
13482
- if (curStep === ARR) {
13483
- stack.pop();
13484
- }
13485
-
13486
- // Array with element
13487
- if (curStep === ARR_VAL) {
13488
- // Pop ARR_VAL, ARR
13489
- stack.pop();
13490
- stack.pop();
13491
- }
13492
-
13493
- // Array with element
13494
- if (curStep === ARR_VAL_COMPLETED) {
13495
- // Pop ARR_VAL_COMPLETED, ARR_VAL, ARR
13496
- stack.pop();
13497
- stack.pop();
13498
- stack.pop();
13499
- }
13500
-
13501
- // if was obj value, complete it
13502
- if (stack[stack.length - 1] === OBJ_VAL) {
13503
- stack.push(OBJ_VAL_COMPLETED);
13504
- }
13505
-
13506
- // if was arr value, complete it
13507
- if (stack[stack.length - 1] === ARR_VAL) {
13508
- stack.push(ARR_VAL_COMPLETED);
13509
- }
13510
- }
13511
-
13512
- function _isEscaped(str, pos) {
13513
- const previousChar = str[pos - 1];
13514
-
13515
- return previousChar === '\\' && !_isEscaped(str, pos - 1);
13516
- }
13517
-
13518
- /* eslint-disable max-lines */
13519
-
13520
- /**
13521
- * Takes an incomplete JSON string, and returns a hopefully valid JSON string.
13522
- * Note that this _can_ fail, so you should check the return value is valid JSON.
13523
- */
13524
- function fixJson(incompleteJson) {
13525
- const stack = evaluateJson(incompleteJson);
13526
-
13527
- return completeJson(incompleteJson, stack);
13528
- }
13529
-
13530
12562
  /** Get the size of a body. */
13531
12563
  function getBodySize(
13532
12564
  body,
@@ -13620,68 +12652,51 @@ function makeNetworkReplayBreadcrumb(
13620
12652
  return result;
13621
12653
  }
13622
12654
 
13623
- /** Build the request or response part of a replay network breadcrumb that was skipped. */
13624
- function buildSkippedNetworkRequestOrResponse(bodySize) {
13625
- return {
13626
- headers: {},
13627
- size: bodySize,
13628
- _meta: {
13629
- warnings: ['URL_SKIPPED'],
13630
- },
13631
- };
12655
+ /** Get either a JSON network body, or a text representation. */
12656
+ function getNetworkBody(bodyText) {
12657
+ if (!bodyText) {
12658
+ return;
12659
+ }
12660
+
12661
+ try {
12662
+ return JSON.parse(bodyText);
12663
+ } catch (e2) {
12664
+ // return text
12665
+ }
12666
+
12667
+ return bodyText;
13632
12668
  }
13633
12669
 
13634
12670
  /** Build the request or response part of a replay network breadcrumb. */
13635
12671
  function buildNetworkRequestOrResponse(
13636
- headers,
13637
12672
  bodySize,
13638
12673
  body,
13639
12674
  ) {
13640
- if (!bodySize && Object.keys(headers).length === 0) {
13641
- return undefined;
13642
- }
13643
-
13644
12675
  if (!bodySize) {
13645
- return {
13646
- headers,
13647
- };
12676
+ return undefined;
13648
12677
  }
13649
12678
 
13650
12679
  if (!body) {
13651
12680
  return {
13652
- headers,
13653
12681
  size: bodySize,
13654
12682
  };
13655
12683
  }
13656
12684
 
13657
12685
  const info = {
13658
- headers,
13659
12686
  size: bodySize,
13660
12687
  };
13661
12688
 
13662
- const { body: normalizedBody, warnings } = normalizeNetworkBody(body);
13663
- info.body = normalizedBody;
13664
- if (warnings.length > 0) {
12689
+ if (bodySize < NETWORK_BODY_MAX_SIZE) {
12690
+ info.body = body;
12691
+ } else {
13665
12692
  info._meta = {
13666
- warnings,
12693
+ errors: ['MAX_BODY_SIZE_EXCEEDED'],
13667
12694
  };
13668
12695
  }
13669
12696
 
13670
12697
  return info;
13671
12698
  }
13672
12699
 
13673
- /** Filter a set of headers */
13674
- function getAllowedHeaders(headers, allowedHeaders) {
13675
- return Object.keys(headers).reduce((filteredHeaders, key) => {
13676
- const normalizedKey = key.toLowerCase();
13677
- // Avoid putting empty strings into the headers
13678
- if (allowedHeaders.includes(normalizedKey) && headers[key]) {
13679
- filteredHeaders[normalizedKey] = headers[key];
13680
- }
13681
- return filteredHeaders;
13682
- }, {});
13683
- }
13684
-
13685
12700
  function _serializeFormData(formData) {
13686
12701
  // This is a bit simplified, but gives us a decent estimate
13687
12702
  // This converts e.g. { name: 'Anne Smith', age: 13 } to 'name=Anne+Smith&age=13'
@@ -13689,78 +12704,6 @@ function _serializeFormData(formData) {
13689
12704
  return new URLSearchParams(formData).toString();
13690
12705
  }
13691
12706
 
13692
- function normalizeNetworkBody(body)
13693
-
13694
- {
13695
- if (!body || typeof body !== 'string') {
13696
- return {
13697
- body,
13698
- warnings: [],
13699
- };
13700
- }
13701
-
13702
- const exceedsSizeLimit = body.length > NETWORK_BODY_MAX_SIZE;
13703
-
13704
- if (_strIsProbablyJson(body)) {
13705
- try {
13706
- const json = exceedsSizeLimit ? fixJson(body.slice(0, NETWORK_BODY_MAX_SIZE)) : body;
13707
- const normalizedBody = JSON.parse(json);
13708
- return {
13709
- body: normalizedBody,
13710
- warnings: exceedsSizeLimit ? ['JSON_TRUNCATED'] : [],
13711
- };
13712
- } catch (e3) {
13713
- return {
13714
- body: exceedsSizeLimit ? `${body.slice(0, NETWORK_BODY_MAX_SIZE)}…` : body,
13715
- warnings: exceedsSizeLimit ? ['INVALID_JSON', 'TEXT_TRUNCATED'] : ['INVALID_JSON'],
13716
- };
13717
- }
13718
- }
13719
-
13720
- return {
13721
- body: exceedsSizeLimit ? `${body.slice(0, NETWORK_BODY_MAX_SIZE)}…` : body,
13722
- warnings: exceedsSizeLimit ? ['TEXT_TRUNCATED'] : [],
13723
- };
13724
- }
13725
-
13726
- function _strIsProbablyJson(str) {
13727
- const first = str[0];
13728
- const last = str[str.length - 1];
13729
-
13730
- // Simple check: If this does not start & end with {} or [], it's not JSON
13731
- return (first === '[' && last === ']') || (first === '{' && last === '}');
13732
- }
13733
-
13734
- /** Match an URL against a list of strings/Regex. */
13735
- function urlMatches(url, urls) {
13736
- const fullUrl = getFullUrl(url);
13737
-
13738
- return stringMatchesSomePattern(fullUrl, urls);
13739
- }
13740
-
13741
- /** exported for tests */
13742
- function getFullUrl(url, baseURI = WINDOW.document.baseURI) {
13743
- // Short circuit for common cases:
13744
- if (url.startsWith('http://') || url.startsWith('https://') || url.startsWith(WINDOW.location.origin)) {
13745
- return url;
13746
- }
13747
- const fixedUrl = new URL(url, baseURI);
13748
-
13749
- // If these do not match, we are not dealing with a relative URL, so just return it
13750
- if (fixedUrl.origin !== new URL(baseURI).origin) {
13751
- return url;
13752
- }
13753
-
13754
- const fullUrl = fixedUrl.href;
13755
-
13756
- // Remove trailing slashes, if they don't match the original URL
13757
- if (!url.endsWith('/') && fullUrl.endsWith('/')) {
13758
- return fullUrl.slice(0, -1);
13759
- }
13760
-
13761
- return fullUrl;
13762
- }
13763
-
13764
12707
  /**
13765
12708
  * Capture a fetch breadcrumb to a replay.
13766
12709
  * This adds additional data (where approriate).
@@ -13768,9 +12711,7 @@ function getFullUrl(url, baseURI = WINDOW.document.baseURI) {
13768
12711
  async function captureFetchBreadcrumbToReplay(
13769
12712
  breadcrumb,
13770
12713
  hint,
13771
- options
13772
-
13773
- ,
12714
+ options,
13774
12715
  ) {
13775
12716
  try {
13776
12717
  const data = await _prepareFetchData(breadcrumb, hint, options);
@@ -13797,7 +12738,6 @@ function enrichFetchBreadcrumb(
13797
12738
 
13798
12739
  const body = _getFetchRequestArgBody(input);
13799
12740
  const reqSize = getBodySize(body, options.textEncoder);
13800
-
13801
12741
  const resSize = response ? parseContentLengthHeader(response.headers.get('content-length')) : undefined;
13802
12742
 
13803
12743
  if (reqSize !== undefined) {
@@ -13811,109 +12751,97 @@ function enrichFetchBreadcrumb(
13811
12751
  async function _prepareFetchData(
13812
12752
  breadcrumb,
13813
12753
  hint,
13814
- options
13815
-
13816
- ,
12754
+ options,
13817
12755
  ) {
13818
12756
  const { startTimestamp, endTimestamp } = hint;
13819
12757
 
13820
12758
  const {
13821
12759
  url,
13822
12760
  method,
13823
- status_code: statusCode = 0,
12761
+ status_code: statusCode,
13824
12762
  request_body_size: requestBodySize,
13825
12763
  response_body_size: responseBodySize,
13826
12764
  } = breadcrumb.data;
13827
12765
 
13828
- const captureDetails = urlMatches(url, options.networkDetailAllowUrls);
13829
-
13830
- const request = captureDetails
13831
- ? _getRequestInfo(options, hint.input, requestBodySize)
13832
- : buildSkippedNetworkRequestOrResponse(requestBodySize);
13833
- const response = await _getResponseInfo(captureDetails, options, hint.response, responseBodySize);
12766
+ const request = _getRequestInfo(options, hint.input, requestBodySize);
12767
+ const response = await _getResponseInfo(options, hint.response, responseBodySize);
13834
12768
 
13835
12769
  return {
13836
12770
  startTimestamp,
13837
12771
  endTimestamp,
13838
12772
  url,
13839
12773
  method,
13840
- statusCode,
12774
+ statusCode: statusCode || 0,
13841
12775
  request,
13842
12776
  response,
13843
12777
  };
13844
12778
  }
13845
12779
 
13846
12780
  function _getRequestInfo(
13847
- { networkCaptureBodies, networkRequestHeaders },
12781
+ { captureBodies },
13848
12782
  input,
13849
12783
  requestBodySize,
13850
12784
  ) {
13851
- const headers = getRequestHeaders(input, networkRequestHeaders);
13852
-
13853
- if (!networkCaptureBodies) {
13854
- return buildNetworkRequestOrResponse(headers, requestBodySize, undefined);
12785
+ if (!captureBodies) {
12786
+ return buildNetworkRequestOrResponse(requestBodySize, undefined);
13855
12787
  }
13856
12788
 
13857
12789
  // We only want to transmit string or string-like bodies
13858
12790
  const requestBody = _getFetchRequestArgBody(input);
13859
- const bodyStr = getBodyString(requestBody);
13860
- return buildNetworkRequestOrResponse(headers, requestBodySize, bodyStr);
12791
+ const body = getNetworkBody(getBodyString(requestBody));
12792
+ return buildNetworkRequestOrResponse(requestBodySize, body);
13861
12793
  }
13862
12794
 
13863
12795
  async function _getResponseInfo(
13864
- captureDetails,
13865
- {
13866
- networkCaptureBodies,
13867
- textEncoder,
13868
- networkResponseHeaders,
13869
- }
13870
-
13871
- ,
12796
+ { captureBodies, textEncoder },
13872
12797
  response,
13873
12798
  responseBodySize,
13874
12799
  ) {
13875
- if (!captureDetails && responseBodySize !== undefined) {
13876
- return buildSkippedNetworkRequestOrResponse(responseBodySize);
13877
- }
13878
-
13879
- const headers = getAllHeaders(response.headers, networkResponseHeaders);
13880
-
13881
- if (!networkCaptureBodies && responseBodySize !== undefined) {
13882
- return buildNetworkRequestOrResponse(headers, responseBodySize, undefined);
12800
+ if (!captureBodies && responseBodySize !== undefined) {
12801
+ return buildNetworkRequestOrResponse(responseBodySize, undefined);
13883
12802
  }
13884
12803
 
13885
12804
  // Only clone the response if we need to
13886
12805
  try {
13887
12806
  // We have to clone this, as the body can only be read once
13888
12807
  const res = response.clone();
13889
- const bodyText = await _parseFetchBody(res);
12808
+ const { body, bodyText } = await _parseFetchBody(res);
13890
12809
 
13891
12810
  const size =
13892
12811
  bodyText && bodyText.length && responseBodySize === undefined
13893
12812
  ? getBodySize(bodyText, textEncoder)
13894
12813
  : responseBodySize;
13895
12814
 
13896
- if (!captureDetails) {
13897
- return buildSkippedNetworkRequestOrResponse(size);
12815
+ if (captureBodies) {
12816
+ return buildNetworkRequestOrResponse(size, body);
13898
12817
  }
13899
12818
 
13900
- if (networkCaptureBodies) {
13901
- return buildNetworkRequestOrResponse(headers, size, bodyText);
13902
- }
13903
-
13904
- return buildNetworkRequestOrResponse(headers, size, undefined);
12819
+ return buildNetworkRequestOrResponse(size, undefined);
13905
12820
  } catch (e) {
13906
12821
  // fallback
13907
- return buildNetworkRequestOrResponse(headers, responseBodySize, undefined);
12822
+ return buildNetworkRequestOrResponse(responseBodySize, undefined);
13908
12823
  }
13909
12824
  }
13910
12825
 
13911
- async function _parseFetchBody(response) {
12826
+ async function _parseFetchBody(
12827
+ response,
12828
+ ) {
12829
+ let bodyText;
12830
+
13912
12831
  try {
13913
- return await response.text();
12832
+ bodyText = await response.text();
13914
12833
  } catch (e2) {
13915
- return undefined;
12834
+ return {};
13916
12835
  }
12836
+
12837
+ try {
12838
+ const body = JSON.parse(bodyText);
12839
+ return { body, bodyText };
12840
+ } catch (e3) {
12841
+ // just send bodyText
12842
+ }
12843
+
12844
+ return { bodyText, body: bodyText };
13917
12845
  }
13918
12846
 
13919
12847
  function _getFetchRequestArgBody(fetchArgs = []) {
@@ -13925,56 +12853,6 @@ function _getFetchRequestArgBody(fetchArgs = []) {
13925
12853
  return (fetchArgs[1] ).body;
13926
12854
  }
13927
12855
 
13928
- function getAllHeaders(headers, allowedHeaders) {
13929
- const allHeaders = {};
13930
-
13931
- allowedHeaders.forEach(header => {
13932
- if (headers.get(header)) {
13933
- allHeaders[header] = headers.get(header) ;
13934
- }
13935
- });
13936
-
13937
- return allHeaders;
13938
- }
13939
-
13940
- function getRequestHeaders(fetchArgs, allowedHeaders) {
13941
- if (fetchArgs.length === 1 && typeof fetchArgs[0] !== 'string') {
13942
- return getHeadersFromOptions(fetchArgs[0] , allowedHeaders);
13943
- }
13944
-
13945
- if (fetchArgs.length === 2) {
13946
- return getHeadersFromOptions(fetchArgs[1] , allowedHeaders);
13947
- }
13948
-
13949
- return {};
13950
- }
13951
-
13952
- function getHeadersFromOptions(
13953
- input,
13954
- allowedHeaders,
13955
- ) {
13956
- if (!input) {
13957
- return {};
13958
- }
13959
-
13960
- const headers = input.headers;
13961
-
13962
- if (!headers) {
13963
- return {};
13964
- }
13965
-
13966
- if (headers instanceof Headers) {
13967
- return getAllHeaders(headers, allowedHeaders);
13968
- }
13969
-
13970
- // We do not support this, as it is not really documented (anymore?)
13971
- if (Array.isArray(headers)) {
13972
- return {};
13973
- }
13974
-
13975
- return getAllowedHeaders(headers, allowedHeaders);
13976
- }
13977
-
13978
12856
  /**
13979
12857
  * Capture an XHR breadcrumb to a replay.
13980
12858
  * This adds additional data (where approriate).
@@ -14025,12 +12903,12 @@ function _prepareXhrData(
14025
12903
  hint,
14026
12904
  options,
14027
12905
  ) {
14028
- const { startTimestamp, endTimestamp, input, xhr } = hint;
12906
+ const { startTimestamp, endTimestamp, input } = hint;
14029
12907
 
14030
12908
  const {
14031
12909
  url,
14032
12910
  method,
14033
- status_code: statusCode = 0,
12911
+ status_code: statusCode,
14034
12912
  request_body_size: requestBodySize,
14035
12913
  response_body_size: responseBodySize,
14036
12914
  } = breadcrumb.data;
@@ -14039,35 +12917,13 @@ function _prepareXhrData(
14039
12917
  return null;
14040
12918
  }
14041
12919
 
14042
- if (!urlMatches(url, options.networkDetailAllowUrls)) {
14043
- const request = buildSkippedNetworkRequestOrResponse(requestBodySize);
14044
- const response = buildSkippedNetworkRequestOrResponse(responseBodySize);
14045
- return {
14046
- startTimestamp,
14047
- endTimestamp,
14048
- url,
14049
- method,
14050
- statusCode,
14051
- request,
14052
- response,
14053
- };
14054
- }
14055
-
14056
- const xhrInfo = xhr[SENTRY_XHR_DATA_KEY];
14057
- const networkRequestHeaders = xhrInfo
14058
- ? getAllowedHeaders(xhrInfo.request_headers, options.networkRequestHeaders)
14059
- : {};
14060
- const networkResponseHeaders = getAllowedHeaders(getResponseHeaders(xhr), options.networkResponseHeaders);
14061
-
14062
12920
  const request = buildNetworkRequestOrResponse(
14063
- networkRequestHeaders,
14064
12921
  requestBodySize,
14065
- options.networkCaptureBodies ? getBodyString(input) : undefined,
12922
+ options.captureBodies ? getNetworkBody(getBodyString(input)) : undefined,
14066
12923
  );
14067
12924
  const response = buildNetworkRequestOrResponse(
14068
- networkResponseHeaders,
14069
12925
  responseBodySize,
14070
- options.networkCaptureBodies ? hint.xhr.responseText : undefined,
12926
+ options.captureBodies ? getNetworkBody(hint.xhr.responseText) : undefined,
14071
12927
  );
14072
12928
 
14073
12929
  return {
@@ -14075,26 +12931,12 @@ function _prepareXhrData(
14075
12931
  endTimestamp,
14076
12932
  url,
14077
12933
  method,
14078
- statusCode,
12934
+ statusCode: statusCode || 0,
14079
12935
  request,
14080
12936
  response,
14081
12937
  };
14082
12938
  }
14083
12939
 
14084
- function getResponseHeaders(xhr) {
14085
- const headers = xhr.getAllResponseHeaders();
14086
-
14087
- if (!headers) {
14088
- return {};
14089
- }
14090
-
14091
- return headers.split('\r\n').reduce((acc, line) => {
14092
- const [key, value] = line.split(': ');
14093
- acc[key.toLowerCase()] = value;
14094
- return acc;
14095
- }, {});
14096
- }
14097
-
14098
12940
  /**
14099
12941
  * This method does two things:
14100
12942
  * - It enriches the regular XHR/fetch breadcrumbs with request/response size data
@@ -14107,16 +12949,10 @@ function handleNetworkBreadcrumbs(replay) {
14107
12949
  try {
14108
12950
  const textEncoder = new TextEncoder();
14109
12951
 
14110
- const { networkDetailAllowUrls, networkCaptureBodies, networkRequestHeaders, networkResponseHeaders } =
14111
- replay.getOptions();
14112
-
14113
12952
  const options = {
14114
12953
  replay,
14115
12954
  textEncoder,
14116
- networkDetailAllowUrls,
14117
- networkCaptureBodies,
14118
- networkRequestHeaders,
14119
- networkResponseHeaders,
12955
+ captureBodies: replay.getOptions()._experiments.captureNetworkBodies || false,
14120
12956
  };
14121
12957
 
14122
12958
  if (client && client.on) {
@@ -14224,66 +13060,9 @@ function handleScope(scope) {
14224
13060
  return null;
14225
13061
  }
14226
13062
 
14227
- if (newBreadcrumb.category === 'console') {
14228
- return normalizeConsoleBreadcrumb(newBreadcrumb);
14229
- }
14230
-
14231
13063
  return createBreadcrumb(newBreadcrumb);
14232
13064
  }
14233
13065
 
14234
- /** exported for tests only */
14235
- function normalizeConsoleBreadcrumb(breadcrumb) {
14236
- const args = breadcrumb.data && breadcrumb.data.arguments;
14237
-
14238
- if (!Array.isArray(args) || args.length === 0) {
14239
- return createBreadcrumb(breadcrumb);
14240
- }
14241
-
14242
- let isTruncated = false;
14243
-
14244
- // Avoid giant args captures
14245
- const normalizedArgs = args.map(arg => {
14246
- if (!arg) {
14247
- return arg;
14248
- }
14249
- if (typeof arg === 'string') {
14250
- if (arg.length > CONSOLE_ARG_MAX_SIZE) {
14251
- isTruncated = true;
14252
- return `${arg.slice(0, CONSOLE_ARG_MAX_SIZE)}…`;
14253
- }
14254
-
14255
- return arg;
14256
- }
14257
- if (typeof arg === 'object') {
14258
- try {
14259
- const normalizedArg = normalize(arg, 7);
14260
- const stringified = JSON.stringify(normalizedArg);
14261
- if (stringified.length > CONSOLE_ARG_MAX_SIZE) {
14262
- const fixedJson = fixJson(stringified.slice(0, CONSOLE_ARG_MAX_SIZE));
14263
- const json = JSON.parse(fixedJson);
14264
- // We only set this after JSON.parse() was successfull, so we know we didn't run into `catch`
14265
- isTruncated = true;
14266
- return json;
14267
- }
14268
- return normalizedArg;
14269
- } catch (e) {
14270
- // fall back to default
14271
- }
14272
- }
14273
-
14274
- return arg;
14275
- });
14276
-
14277
- return createBreadcrumb({
14278
- ...breadcrumb,
14279
- data: {
14280
- ...breadcrumb.data,
14281
- arguments: normalizedArgs,
14282
- ...(isTruncated ? { _meta: { warnings: ['CONSOLE_ARG_TRUNCATED'] } } : {}),
14283
- },
14284
- });
14285
- }
14286
-
14287
13066
  /**
14288
13067
  * Add global listeners that cannot be removed.
14289
13068
  */
@@ -14308,8 +13087,7 @@ function addGlobalListeners(replay) {
14308
13087
  client.on('afterSendEvent', handleAfterSendEvent(replay));
14309
13088
  client.on('createDsc', (dsc) => {
14310
13089
  const replayId = replay.getSessionId();
14311
- // We do not want to set the DSC when in buffer mode, as that means the replay has not been sent (yet)
14312
- if (replayId && replay.isEnabled() && replay.recordingMode === 'session') {
13090
+ if (replayId) {
14313
13091
  dsc.replay_id = replayId;
14314
13092
  }
14315
13093
  });
@@ -14589,23 +13367,6 @@ function debounce(func, wait, options) {
14589
13367
  return debounced;
14590
13368
  }
14591
13369
 
14592
- /* eslint-disable @typescript-eslint/naming-convention */
14593
-
14594
- var EventType; (function (EventType) {
14595
- const DomContentLoaded = 0; EventType[EventType["DomContentLoaded"] = DomContentLoaded] = "DomContentLoaded";
14596
- const Load = 1; EventType[EventType["Load"] = Load] = "Load";
14597
- const FullSnapshot = 2; EventType[EventType["FullSnapshot"] = FullSnapshot] = "FullSnapshot";
14598
- const IncrementalSnapshot = 3; EventType[EventType["IncrementalSnapshot"] = IncrementalSnapshot] = "IncrementalSnapshot";
14599
- const Meta = 4; EventType[EventType["Meta"] = Meta] = "Meta";
14600
- const Custom = 5; EventType[EventType["Custom"] = Custom] = "Custom";
14601
- const Plugin = 6; EventType[EventType["Plugin"] = Plugin] = "Plugin";
14602
- })(EventType || (EventType = {}));
14603
-
14604
- /**
14605
- * This is a partial copy of rrweb's eventWithTime type which only contains the properties
14606
- * we specifcally need in the SDK.
14607
- */
14608
-
14609
13370
  /**
14610
13371
  * Handler for recording events.
14611
13372
  *
@@ -14634,7 +13395,7 @@ function getHandleRecordingEmit(replay) {
14634
13395
  // when an error occurs. Clear any state that happens before this current
14635
13396
  // checkout. This needs to happen before `addEvent()` which updates state
14636
13397
  // dependent on this reset.
14637
- if (replay.recordingMode === 'buffer' && isCheckout) {
13398
+ if (replay.recordingMode === 'error' && isCheckout) {
14638
13399
  replay.setInitialState();
14639
13400
  }
14640
13401
 
@@ -14648,14 +13409,6 @@ function getHandleRecordingEmit(replay) {
14648
13409
  return false;
14649
13410
  }
14650
13411
 
14651
- // Additionally, create a meta event that will capture certain SDK settings.
14652
- // In order to handle buffer mode, this needs to either be done when we
14653
- // receive checkout events or at flush time.
14654
- //
14655
- // `isCheckout` is always true, but want to be explicit that it should
14656
- // only be added for checkouts
14657
- void addSettingsEvent(replay, isCheckout);
14658
-
14659
13412
  // If there is a previousSessionId after a full snapshot occurs, then
14660
13413
  // the replay session was started due to session expiration. The new session
14661
13414
  // is started before triggering a new checkout and contains the id
@@ -14666,10 +13419,10 @@ function getHandleRecordingEmit(replay) {
14666
13419
  return true;
14667
13420
  }
14668
13421
 
14669
- // When in buffer mode, make sure we adjust the session started date to the current earliest event of the buffer
14670
- // this should usually be the timestamp of the checkout event, but to be safe...
14671
- if (replay.recordingMode === 'buffer' && replay.session && replay.eventBuffer) {
14672
- const earliestEvent = replay.eventBuffer.getEarliestTimestamp();
13422
+ // See note above re: session start needs to reflect the most recent
13423
+ // checkout.
13424
+ if (replay.recordingMode === 'error' && replay.session) {
13425
+ const { earliestEvent } = replay.getContext();
14673
13426
  if (earliestEvent) {
14674
13427
  replay.session.started = earliestEvent;
14675
13428
 
@@ -14693,46 +13446,6 @@ function getHandleRecordingEmit(replay) {
14693
13446
  };
14694
13447
  }
14695
13448
 
14696
- /**
14697
- * Exported for tests
14698
- */
14699
- function createOptionsEvent(replay) {
14700
- const options = replay.getOptions();
14701
- return {
14702
- type: EventType.Custom,
14703
- timestamp: Date.now(),
14704
- data: {
14705
- tag: 'options',
14706
- payload: {
14707
- sessionSampleRate: options.sessionSampleRate,
14708
- errorSampleRate: options.errorSampleRate,
14709
- useCompressionOption: options.useCompression,
14710
- blockAllMedia: options.blockAllMedia,
14711
- maskAllText: options.maskAllText,
14712
- maskAllInputs: options.maskAllInputs,
14713
- useCompression: replay.eventBuffer ? replay.eventBuffer.type === 'worker' : false,
14714
- networkDetailHasUrls: options.networkDetailAllowUrls.length > 0,
14715
- networkCaptureBodies: options.networkCaptureBodies,
14716
- networkRequestHasHeaders: options.networkRequestHeaders.length > 0,
14717
- networkResponseHasHeaders: options.networkResponseHeaders.length > 0,
14718
- },
14719
- },
14720
- };
14721
- }
14722
-
14723
- /**
14724
- * Add a "meta" event that contains a simplified view on current configuration
14725
- * options. This should only be included on the first segment of a recording.
14726
- */
14727
- function addSettingsEvent(replay, isCheckout) {
14728
- // Only need to add this event when sending the first segment
14729
- if (!isCheckout || !replay.session || replay.session.segmentId !== 0) {
14730
- return Promise.resolve(null);
14731
- }
14732
-
14733
- return addEvent(replay, createOptionsEvent(replay), false);
14734
- }
14735
-
14736
13449
  /**
14737
13450
  * Create a replay envelope ready to be sent.
14738
13451
  * This includes both the replay event, as well as the recording data.
@@ -14844,9 +13557,11 @@ async function sendReplayRequest({
14844
13557
  recordingData,
14845
13558
  replayId,
14846
13559
  segmentId: segment_id,
13560
+ includeReplayStartTimestamp,
14847
13561
  eventContext,
14848
13562
  timestamp,
14849
13563
  session,
13564
+ options,
14850
13565
  }) {
14851
13566
  const preparedRecordingData = prepareRecordingData({
14852
13567
  recordingData,
@@ -14868,8 +13583,9 @@ async function sendReplayRequest({
14868
13583
  }
14869
13584
 
14870
13585
  const baseEvent = {
13586
+ // @ts-ignore private api
14871
13587
  type: REPLAY_EVENT_NAME,
14872
- replay_start_timestamp: initialTimestamp / 1000,
13588
+ ...(includeReplayStartTimestamp ? { replay_start_timestamp: initialTimestamp / 1000 } : {}),
14873
13589
  timestamp: timestamp / 1000,
14874
13590
  error_ids: errorIds,
14875
13591
  trace_ids: traceIds,
@@ -14888,6 +13604,15 @@ async function sendReplayRequest({
14888
13604
  return;
14889
13605
  }
14890
13606
 
13607
+ replayEvent.contexts = {
13608
+ ...replayEvent.contexts,
13609
+ replay: {
13610
+ ...(replayEvent.contexts && replayEvent.contexts.replay),
13611
+ session_sample_rate: options.sessionSampleRate,
13612
+ error_sample_rate: options.errorSampleRate,
13613
+ },
13614
+ };
13615
+
14891
13616
  /*
14892
13617
  For reference, the fully built event looks something like this:
14893
13618
  {
@@ -14918,6 +13643,10 @@ async function sendReplayRequest({
14918
13643
  },
14919
13644
  "sdkProcessingMetadata": {},
14920
13645
  "contexts": {
13646
+ "replay": {
13647
+ "session_sample_rate": 1,
13648
+ "error_sample_rate": 0,
13649
+ },
14921
13650
  },
14922
13651
  }
14923
13652
  */
@@ -15043,11 +13772,9 @@ class ReplayContainer {
15043
13772
  __init2() {this.performanceEvents = [];}
15044
13773
 
15045
13774
  /**
15046
- * Recording can happen in one of three modes:
15047
- * - session: Record the whole session, sending it continuously
15048
- * - buffer: Always keep the last 60s of recording, requires:
15049
- * - having replaysOnErrorSampleRate > 0 to capture replay when an error occurs
15050
- * - or calling `flush()` to send the replay
13775
+ * Recording can happen in one of two modes:
13776
+ * * session: Record the whole session, sending it continuously
13777
+ * * error: Always keep the last 60s of recording, and when an error occurs, send it immediately
15051
13778
  */
15052
13779
  __init3() {this.recordingMode = 'session';}
15053
13780
 
@@ -15056,8 +13783,7 @@ class ReplayContainer {
15056
13783
  * @hidden
15057
13784
  */
15058
13785
  __init4() {this.timeouts = {
15059
- sessionIdlePause: SESSION_IDLE_PAUSE_DURATION,
15060
- sessionIdleExpire: SESSION_IDLE_EXPIRE_DURATION,
13786
+ sessionIdle: SESSION_IDLE_DURATION,
15061
13787
  maxSessionLife: MAX_SESSION_LIFE,
15062
13788
  }; }
15063
13789
 
@@ -15101,6 +13827,7 @@ class ReplayContainer {
15101
13827
  errorIds: new Set(),
15102
13828
  traceIds: new Set(),
15103
13829
  urls: [],
13830
+ earliestEvent: null,
15104
13831
  initialTimestamp: Date.now(),
15105
13832
  initialUrl: '',
15106
13833
  };}
@@ -15110,7 +13837,7 @@ class ReplayContainer {
15110
13837
  recordingOptions,
15111
13838
  }
15112
13839
 
15113
- ) {ReplayContainer.prototype.__init.call(this);ReplayContainer.prototype.__init2.call(this);ReplayContainer.prototype.__init3.call(this);ReplayContainer.prototype.__init4.call(this);ReplayContainer.prototype.__init5.call(this);ReplayContainer.prototype.__init6.call(this);ReplayContainer.prototype.__init7.call(this);ReplayContainer.prototype.__init8.call(this);ReplayContainer.prototype.__init9.call(this);ReplayContainer.prototype.__init10.call(this);ReplayContainer.prototype.__init11.call(this);ReplayContainer.prototype.__init12.call(this);ReplayContainer.prototype.__init13.call(this);ReplayContainer.prototype.__init14.call(this);ReplayContainer.prototype.__init15.call(this);ReplayContainer.prototype.__init16.call(this);ReplayContainer.prototype.__init17.call(this);ReplayContainer.prototype.__init18.call(this);
13840
+ ) {ReplayContainer.prototype.__init.call(this);ReplayContainer.prototype.__init2.call(this);ReplayContainer.prototype.__init3.call(this);ReplayContainer.prototype.__init4.call(this);ReplayContainer.prototype.__init5.call(this);ReplayContainer.prototype.__init6.call(this);ReplayContainer.prototype.__init7.call(this);ReplayContainer.prototype.__init8.call(this);ReplayContainer.prototype.__init9.call(this);ReplayContainer.prototype.__init10.call(this);ReplayContainer.prototype.__init11.call(this);ReplayContainer.prototype.__init12.call(this);ReplayContainer.prototype.__init13.call(this);ReplayContainer.prototype.__init14.call(this);ReplayContainer.prototype.__init15.call(this);ReplayContainer.prototype.__init16.call(this);ReplayContainer.prototype.__init17.call(this);
15114
13841
  this._recordingOptions = recordingOptions;
15115
13842
  this._options = options;
15116
13843
 
@@ -15140,102 +13867,49 @@ class ReplayContainer {
15140
13867
  }
15141
13868
 
15142
13869
  /**
15143
- * Initializes the plugin based on sampling configuration. Should not be
15144
- * called outside of constructor.
13870
+ * Initializes the plugin.
13871
+ *
13872
+ * Creates or loads a session, attaches listeners to varying events (DOM,
13873
+ * _performanceObserver, Recording, Sentry SDK, etc)
15145
13874
  */
15146
- initializeSampling() {
15147
- const { errorSampleRate, sessionSampleRate } = this._options;
15148
-
15149
- // If neither sample rate is > 0, then do nothing - user will need to call one of
15150
- // `start()` or `startBuffering` themselves.
15151
- if (errorSampleRate <= 0 && sessionSampleRate <= 0) {
15152
- return;
15153
- }
15154
-
15155
- // Otherwise if there is _any_ sample rate set, try to load an existing
15156
- // session, or create a new one.
15157
- const isSessionSampled = this._loadAndCheckSession();
13875
+ start() {
13876
+ this.setInitialState();
15158
13877
 
15159
- if (!isSessionSampled) {
15160
- // This should only occur if `errorSampleRate` is 0 and was unsampled for
15161
- // session-based replay. In this case there is nothing to do.
13878
+ if (!this._loadAndCheckSession()) {
15162
13879
  return;
15163
13880
  }
15164
13881
 
13882
+ // If there is no session, then something bad has happened - can't continue
15165
13883
  if (!this.session) {
15166
- // This should not happen, something wrong has occurred
15167
- this._handleException(new Error('Unable to initialize and create session'));
13884
+ this._handleException(new Error('No session found'));
15168
13885
  return;
15169
13886
  }
15170
13887
 
15171
- if (this.session.sampled && this.session.sampled !== 'session') {
15172
- // If not sampled as session-based, then recording mode will be `buffer`
15173
- // Note that we don't explicitly check if `sampled === 'buffer'` because we
15174
- // could have sessions from Session storage that are still `error` from
15175
- // prior SDK version.
15176
- this.recordingMode = 'buffer';
15177
- }
15178
-
15179
- this._initializeRecording();
15180
- }
15181
-
15182
- /**
15183
- * Start a replay regardless of sampling rate. Calling this will always
15184
- * create a new session. Will throw an error if replay is already in progress.
15185
- *
15186
- * Creates or loads a session, attaches listeners to varying events (DOM,
15187
- * _performanceObserver, Recording, Sentry SDK, etc)
15188
- */
15189
- start() {
15190
- if (this._isEnabled && this.recordingMode === 'session') {
15191
- throw new Error('Replay recording is already in progress');
13888
+ if (!this.session.sampled) {
13889
+ // If session was not sampled, then we do not initialize the integration at all.
13890
+ return;
15192
13891
  }
15193
13892
 
15194
- if (this._isEnabled && this.recordingMode === 'buffer') {
15195
- throw new Error('Replay buffering is in progress, call `flush()` to save the replay');
13893
+ // If session is sampled for errors, then we need to set the recordingMode
13894
+ // to 'error', which will configure recording with different options.
13895
+ if (this.session.sampled === 'error') {
13896
+ this.recordingMode = 'error';
15196
13897
  }
15197
13898
 
15198
- const previousSessionId = this.session && this.session.id;
13899
+ // setup() is generally called on page load or manually - in both cases we
13900
+ // should treat it as an activity
13901
+ this._updateSessionActivity();
15199
13902
 
15200
- const { session } = getSession({
15201
- timeouts: this.timeouts,
15202
- stickySession: Boolean(this._options.stickySession),
15203
- currentSession: this.session,
15204
- // This is intentional: create a new session-based replay when calling `start()`
15205
- sessionSampleRate: 1,
15206
- allowBuffering: false,
13903
+ this.eventBuffer = createEventBuffer({
13904
+ useCompression: this._options.useCompression,
15207
13905
  });
15208
13906
 
15209
- session.previousSessionId = previousSessionId;
15210
- this.session = session;
15211
-
15212
- this._initializeRecording();
15213
- }
15214
-
15215
- /**
15216
- * Start replay buffering. Buffers until `flush()` is called or, if
15217
- * `replaysOnErrorSampleRate` > 0, an error occurs.
15218
- */
15219
- startBuffering() {
15220
- if (this._isEnabled) {
15221
- throw new Error('Replay recording is already in progress');
15222
- }
15223
-
15224
- const previousSessionId = this.session && this.session.id;
15225
-
15226
- const { session } = getSession({
15227
- timeouts: this.timeouts,
15228
- stickySession: Boolean(this._options.stickySession),
15229
- currentSession: this.session,
15230
- sessionSampleRate: 0,
15231
- allowBuffering: true,
15232
- });
13907
+ this._addListeners();
15233
13908
 
15234
- session.previousSessionId = previousSessionId;
15235
- this.session = session;
13909
+ // Need to set as enabled before we start recording, as `record()` can trigger a flush with a new checkout
13910
+ this._isEnabled = true;
15236
13911
 
15237
- this.recordingMode = 'buffer';
15238
- this._initializeRecording();
13912
+ this.startRecording();
15239
13913
  }
15240
13914
 
15241
13915
  /**
@@ -15250,7 +13924,7 @@ class ReplayContainer {
15250
13924
  // When running in error sampling mode, we need to overwrite `checkoutEveryNms`
15251
13925
  // Without this, it would record forever, until an error happens, which we don't want
15252
13926
  // instead, we'll always keep the last 60 seconds of replay before an error happened
15253
- ...(this.recordingMode === 'buffer' && { checkoutEveryNms: BUFFER_CHECKOUT_TIME }),
13927
+ ...(this.recordingMode === 'error' && { checkoutEveryNms: ERROR_CHECKOUT_TIME }),
15254
13928
  emit: getHandleRecordingEmit(this),
15255
13929
  onMutation: this._onMutationHandler,
15256
13930
  });
@@ -15261,18 +13935,17 @@ class ReplayContainer {
15261
13935
 
15262
13936
  /**
15263
13937
  * Stops the recording, if it was running.
15264
- *
15265
- * Returns true if it was previously stopped, or is now stopped,
15266
- * otherwise false.
13938
+ * Returns true if it was stopped, else false.
15267
13939
  */
15268
13940
  stopRecording() {
15269
13941
  try {
15270
13942
  if (this._stopRecording) {
15271
13943
  this._stopRecording();
15272
13944
  this._stopRecording = undefined;
13945
+ return true;
15273
13946
  }
15274
13947
 
15275
- return true;
13948
+ return false;
15276
13949
  } catch (err) {
15277
13950
  this._handleException(err);
15278
13951
  return false;
@@ -15283,7 +13956,7 @@ class ReplayContainer {
15283
13956
  * Currently, this needs to be manually called (e.g. for tests). Sentry SDK
15284
13957
  * does not support a teardown
15285
13958
  */
15286
- async stop(reason) {
13959
+ stop(reason) {
15287
13960
  if (!this._isEnabled) {
15288
13961
  return;
15289
13962
  }
@@ -15299,24 +13972,12 @@ class ReplayContainer {
15299
13972
  log(msg);
15300
13973
  }
15301
13974
 
15302
- // We can't move `_isEnabled` after awaiting a flush, otherwise we can
15303
- // enter into an infinite loop when `stop()` is called while flushing.
15304
13975
  this._isEnabled = false;
15305
13976
  this._removeListeners();
15306
13977
  this.stopRecording();
15307
-
15308
- this._debouncedFlush.cancel();
15309
- // See comment above re: `_isEnabled`, we "force" a flush, ignoring the
15310
- // `_isEnabled` state of the plugin since it was disabled above.
15311
- await this._flush({ force: true });
15312
-
15313
- // After flush, destroy event buffer
15314
13978
  this.eventBuffer && this.eventBuffer.destroy();
15315
13979
  this.eventBuffer = null;
15316
-
15317
- // Clear session from session storage, note this means if a new session
15318
- // is started after, it will not have `previousSessionId`
15319
- clearSession(this);
13980
+ this._debouncedFlush.cancel();
15320
13981
  } catch (err) {
15321
13982
  this._handleException(err);
15322
13983
  }
@@ -15347,45 +14008,6 @@ class ReplayContainer {
15347
14008
  this.startRecording();
15348
14009
  }
15349
14010
 
15350
- /**
15351
- * If not in "session" recording mode, flush event buffer which will create a new replay.
15352
- * Unless `continueRecording` is false, the replay will continue to record and
15353
- * behave as a "session"-based replay.
15354
- *
15355
- * Otherwise, queue up a flush.
15356
- */
15357
- async sendBufferedReplayOrFlush({ continueRecording = true } = {}) {
15358
- if (this.recordingMode === 'session') {
15359
- return this.flushImmediate();
15360
- }
15361
-
15362
- // Allow flush to complete before resuming as a session recording, otherwise
15363
- // the checkout from `startRecording` may be included in the payload.
15364
- // Prefer to keep the error replay as a separate (and smaller) segment
15365
- // than the session replay.
15366
- await this.flushImmediate();
15367
-
15368
- const hasStoppedRecording = this.stopRecording();
15369
-
15370
- if (!continueRecording || !hasStoppedRecording) {
15371
- return;
15372
- }
15373
-
15374
- // Re-start recording, but in "session" recording mode
15375
-
15376
- // Reset all "capture on error" configuration before
15377
- // starting a new recording
15378
- this.recordingMode = 'session';
15379
-
15380
- // Once this session ends, we do not want to refresh it
15381
- if (this.session) {
15382
- this.session.shouldRefresh = false;
15383
- this._maybeSaveSession();
15384
- }
15385
-
15386
- this.startRecording();
15387
- }
15388
-
15389
14011
  /**
15390
14012
  * We want to batch uploads of replay events. Save events only if
15391
14013
  * `<flushMinDelay>` milliseconds have elapsed since the last event
@@ -15395,12 +14017,12 @@ class ReplayContainer {
15395
14017
  * processing and hand back control to caller.
15396
14018
  */
15397
14019
  addUpdate(cb) {
15398
- // We need to always run `cb` (e.g. in the case of `this.recordingMode == 'buffer'`)
14020
+ // We need to always run `cb` (e.g. in the case of `this.recordingMode == 'error'`)
15399
14021
  const cbResult = cb();
15400
14022
 
15401
14023
  // If this option is turned on then we will only want to call `flush`
15402
14024
  // explicitly
15403
- if (this.recordingMode === 'buffer') {
14025
+ if (this.recordingMode === 'error') {
15404
14026
  return;
15405
14027
  }
15406
14028
 
@@ -15472,12 +14094,12 @@ class ReplayContainer {
15472
14094
  const oldSessionId = this.getSessionId();
15473
14095
 
15474
14096
  // Prevent starting a new session if the last user activity is older than
15475
- // SESSION_IDLE_PAUSE_DURATION. Otherwise non-user activity can trigger a new
14097
+ // SESSION_IDLE_DURATION. Otherwise non-user activity can trigger a new
15476
14098
  // session+recording. This creates noisy replays that do not have much
15477
14099
  // content in them.
15478
14100
  if (
15479
14101
  this._lastActivity &&
15480
- isExpired(this._lastActivity, this.timeouts.sessionIdlePause) &&
14102
+ isExpired(this._lastActivity, this.timeouts.sessionIdle) &&
15481
14103
  this.session &&
15482
14104
  this.session.sampled === 'session'
15483
14105
  ) {
@@ -15527,30 +14149,6 @@ class ReplayContainer {
15527
14149
  this._context.urls.push(url);
15528
14150
  }
15529
14151
 
15530
- /**
15531
- * Initialize and start all listeners to varying events (DOM,
15532
- * Performance Observer, Recording, Sentry SDK, etc)
15533
- */
15534
- _initializeRecording() {
15535
- this.setInitialState();
15536
-
15537
- // this method is generally called on page load or manually - in both cases
15538
- // we should treat it as an activity
15539
- this._updateSessionActivity();
15540
-
15541
- this.eventBuffer = createEventBuffer({
15542
- useCompression: this._options.useCompression,
15543
- });
15544
-
15545
- this._removeListeners();
15546
- this._addListeners();
15547
-
15548
- // Need to set as enabled before we start recording, as `record()` can trigger a flush with a new checkout
15549
- this._isEnabled = true;
15550
-
15551
- this.startRecording();
15552
- }
15553
-
15554
14152
  /** A wrapper to conditionally capture exceptions. */
15555
14153
  _handleException(error) {
15556
14154
  (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.error('[Replay]', error);
@@ -15570,7 +14168,7 @@ class ReplayContainer {
15570
14168
  stickySession: Boolean(this._options.stickySession),
15571
14169
  currentSession: this.session,
15572
14170
  sessionSampleRate: this._options.sessionSampleRate,
15573
- allowBuffering: this._options.errorSampleRate > 0,
14171
+ errorSampleRate: this._options.errorSampleRate,
15574
14172
  });
15575
14173
 
15576
14174
  // If session was newly created (i.e. was not loaded from storage), then
@@ -15587,7 +14185,7 @@ class ReplayContainer {
15587
14185
  this.session = session;
15588
14186
 
15589
14187
  if (!this.session.sampled) {
15590
- void this.stop('session unsampled');
14188
+ this.stop('session unsampled');
15591
14189
  return false;
15592
14190
  }
15593
14191
 
@@ -15602,7 +14200,6 @@ class ReplayContainer {
15602
14200
  WINDOW.document.addEventListener('visibilitychange', this._handleVisibilityChange);
15603
14201
  WINDOW.addEventListener('blur', this._handleWindowBlur);
15604
14202
  WINDOW.addEventListener('focus', this._handleWindowFocus);
15605
- WINDOW.addEventListener('keydown', this._handleKeyboardEvent);
15606
14203
 
15607
14204
  // There is no way to remove these listeners, so ensure they are only added once
15608
14205
  if (!this._hasInitializedCoreListeners) {
@@ -15631,7 +14228,6 @@ class ReplayContainer {
15631
14228
 
15632
14229
  WINDOW.removeEventListener('blur', this._handleWindowBlur);
15633
14230
  WINDOW.removeEventListener('focus', this._handleWindowFocus);
15634
- WINDOW.removeEventListener('keydown', this._handleKeyboardEvent);
15635
14231
 
15636
14232
  if (this._performanceObserver) {
15637
14233
  this._performanceObserver.disconnect();
@@ -15682,11 +14278,6 @@ class ReplayContainer {
15682
14278
  this._doChangeToForegroundTasks(breadcrumb);
15683
14279
  };}
15684
14280
 
15685
- /** Ensure page remains active when a key is pressed. */
15686
- __init16() {this._handleKeyboardEvent = (event) => {
15687
- handleKeyboardEvent(this, event);
15688
- };}
15689
-
15690
14281
  /**
15691
14282
  * Tasks to run when we consider a page to be hidden (via blurring and/or visibility)
15692
14283
  */
@@ -15718,7 +14309,7 @@ class ReplayContainer {
15718
14309
  const isSessionActive = this.checkAndHandleExpiredSession();
15719
14310
 
15720
14311
  if (!isSessionActive) {
15721
- // If the user has come back to the page within SESSION_IDLE_PAUSE_DURATION
14312
+ // If the user has come back to the page within SESSION_IDLE_DURATION
15722
14313
  // ms, we will re-use the existing session, otherwise create a new
15723
14314
  // session
15724
14315
  (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.log('[Replay] Document has become active, but session has expired');
@@ -15766,7 +14357,7 @@ class ReplayContainer {
15766
14357
  _createCustomBreadcrumb(breadcrumb) {
15767
14358
  this.addUpdate(() => {
15768
14359
  void addEvent(this, {
15769
- type: EventType$1.Custom,
14360
+ type: EventType.Custom,
15770
14361
  timestamp: breadcrumb.timestamp || 0,
15771
14362
  data: {
15772
14363
  tag: 'breadcrumb',
@@ -15792,7 +14383,7 @@ class ReplayContainer {
15792
14383
  * Only flush if `this.recordingMode === 'session'`
15793
14384
  */
15794
14385
  _conditionalFlush() {
15795
- if (this.recordingMode === 'buffer') {
14386
+ if (this.recordingMode === 'error') {
15796
14387
  return;
15797
14388
  }
15798
14389
 
@@ -15807,35 +14398,22 @@ class ReplayContainer {
15807
14398
  this._context.errorIds.clear();
15808
14399
  this._context.traceIds.clear();
15809
14400
  this._context.urls = [];
15810
- }
15811
-
15812
- /** Update the initial timestamp based on the buffer content. */
15813
- _updateInitialTimestampFromEventBuffer() {
15814
- const { session, eventBuffer } = this;
15815
- if (!session || !eventBuffer) {
15816
- return;
15817
- }
15818
-
15819
- // we only ever update this on the initial segment
15820
- if (session.segmentId) {
15821
- return;
15822
- }
15823
-
15824
- const earliestEvent = eventBuffer.getEarliestTimestamp();
15825
- if (earliestEvent && earliestEvent < this._context.initialTimestamp) {
15826
- this._context.initialTimestamp = earliestEvent;
15827
- }
14401
+ this._context.earliestEvent = null;
15828
14402
  }
15829
14403
 
15830
14404
  /**
15831
14405
  * Return and clear _context
15832
14406
  */
15833
14407
  _popEventContext() {
14408
+ if (this._context.earliestEvent && this._context.earliestEvent < this._context.initialTimestamp) {
14409
+ this._context.initialTimestamp = this._context.earliestEvent;
14410
+ }
14411
+
15834
14412
  const _context = {
15835
14413
  initialTimestamp: this._context.initialTimestamp,
15836
14414
  initialUrl: this._context.initialUrl,
15837
- errorIds: Array.from(this._context.errorIds),
15838
- traceIds: Array.from(this._context.traceIds),
14415
+ errorIds: Array.from(this._context.errorIds).filter(Boolean),
14416
+ traceIds: Array.from(this._context.traceIds).filter(Boolean),
15839
14417
  urls: this._context.urls,
15840
14418
  };
15841
14419
 
@@ -15874,9 +14452,6 @@ class ReplayContainer {
15874
14452
  }
15875
14453
 
15876
14454
  try {
15877
- // This uses the data from the eventBuffer, so we need to call this before `finish()
15878
- this._updateInitialTimestampFromEventBuffer();
15879
-
15880
14455
  // Note this empties the event buffer regardless of outcome of sending replay
15881
14456
  const recordingData = await this.eventBuffer.finish();
15882
14457
 
@@ -15892,6 +14467,7 @@ class ReplayContainer {
15892
14467
  replayId,
15893
14468
  recordingData,
15894
14469
  segmentId,
14470
+ includeReplayStartTimestamp: segmentId === 0,
15895
14471
  eventContext,
15896
14472
  session: this.session,
15897
14473
  options: this.getOptions(),
@@ -15903,7 +14479,7 @@ class ReplayContainer {
15903
14479
  // This means we retried 3 times and all of them failed,
15904
14480
  // or we ran into a problem we don't want to retry, like rate limiting.
15905
14481
  // In this case, we want to completely stop the replay - otherwise, we may get inconsistent segments
15906
- void this.stop('sendReplay');
14482
+ this.stop('sendReplay');
15907
14483
 
15908
14484
  const client = getCurrentHub().getClient();
15909
14485
 
@@ -15917,12 +14493,8 @@ class ReplayContainer {
15917
14493
  * Flush recording data to Sentry. Creates a lock so that only a single flush
15918
14494
  * can be active at a time. Do not call this directly.
15919
14495
  */
15920
- __init17() {this._flush = async ({
15921
- force = false,
15922
- }
15923
-
15924
- = {}) => {
15925
- if (!this._isEnabled && !force) {
14496
+ __init16() {this._flush = async () => {
14497
+ if (!this._isEnabled) {
15926
14498
  // This can happen if e.g. the replay was stopped because of exceeding the retry limit
15927
14499
  return;
15928
14500
  }
@@ -15972,7 +14544,7 @@ class ReplayContainer {
15972
14544
  }
15973
14545
 
15974
14546
  /** Handler for rrweb.record.onMutation */
15975
- __init18() {this._onMutationHandler = (mutations) => {
14547
+ __init17() {this._onMutationHandler = (mutations) => {
15976
14548
  const count = mutations.length;
15977
14549
 
15978
14550
  const mutationLimit = this._options._experiments.mutationLimit || 0;
@@ -16106,8 +14678,6 @@ function isElectronNodeRenderer() {
16106
14678
  const MEDIA_SELECTORS =
16107
14679
  'img,image,svg,video,object,picture,embed,map,audio,link[rel="icon"],link[rel="apple-touch-icon"]';
16108
14680
 
16109
- const DEFAULT_NETWORK_HEADERS = ['content-length', 'content-type', 'accept'];
16110
-
16111
14681
  let _initialized = false;
16112
14682
 
16113
14683
  /**
@@ -16148,11 +14718,6 @@ class Replay {
16148
14718
  maskAllInputs = true,
16149
14719
  blockAllMedia = true,
16150
14720
 
16151
- networkDetailAllowUrls = [],
16152
- networkCaptureBodies = true,
16153
- networkRequestHeaders = [],
16154
- networkResponseHeaders = [],
16155
-
16156
14721
  mask = [],
16157
14722
  unmask = [],
16158
14723
  block = [],
@@ -16211,13 +14776,6 @@ class Replay {
16211
14776
  errorSampleRate,
16212
14777
  useCompression,
16213
14778
  blockAllMedia,
16214
- maskAllInputs,
16215
- maskAllText,
16216
- networkDetailAllowUrls,
16217
- networkCaptureBodies,
16218
- networkRequestHeaders: _getMergedNetworkHeaders(networkRequestHeaders),
16219
- networkResponseHeaders: _getMergedNetworkHeaders(networkResponseHeaders),
16220
-
16221
14779
  _experiments,
16222
14780
  };
16223
14781
 
@@ -16271,7 +14829,14 @@ Sentry.init({ replaysOnErrorSampleRate: ${errorSampleRate} })`,
16271
14829
  }
16272
14830
 
16273
14831
  /**
16274
- * Setup and initialize replay container
14832
+ * We previously used to create a transaction in `setupOnce` and it would
14833
+ * potentially create a transaction before some native SDK integrations have run
14834
+ * and applied their own global event processor. An example is:
14835
+ * https://github.com/getsentry/sentry-javascript/blob/b47ceafbdac7f8b99093ce6023726ad4687edc48/packages/browser/src/integrations/useragent.ts
14836
+ *
14837
+ * So we call `replay.setup` in next event loop as a workaround to wait for other
14838
+ * global event processors to finish. This is no longer needed, but keeping it
14839
+ * here to avoid any future issues.
16275
14840
  */
16276
14841
  setupOnce() {
16277
14842
  if (!isBrowser()) {
@@ -16280,20 +14845,12 @@ Sentry.init({ replaysOnErrorSampleRate: ${errorSampleRate} })`,
16280
14845
 
16281
14846
  this._setup();
16282
14847
 
16283
- // Once upon a time, we tried to create a transaction in `setupOnce` and it would
16284
- // potentially create a transaction before some native SDK integrations have run
16285
- // and applied their own global event processor. An example is:
16286
- // https://github.com/getsentry/sentry-javascript/blob/b47ceafbdac7f8b99093ce6023726ad4687edc48/packages/browser/src/integrations/useragent.ts
16287
- //
16288
- // So we call `this._initialize()` in next event loop as a workaround to wait for other
16289
- // global event processors to finish. This is no longer needed, but keeping it
16290
- // here to avoid any future issues.
16291
- setTimeout(() => this._initialize());
14848
+ // XXX: See method comments above
14849
+ setTimeout(() => this.start());
16292
14850
  }
16293
14851
 
16294
14852
  /**
16295
- * Start a replay regardless of sampling rate. Calling this will always
16296
- * create a new session. Will throw an error if replay is already in progress.
14853
+ * Initializes the plugin.
16297
14854
  *
16298
14855
  * Creates or loads a session, attaches listeners to varying events (DOM,
16299
14856
  * PerformanceObserver, Recording, Sentry SDK, etc)
@@ -16306,64 +14863,27 @@ Sentry.init({ replaysOnErrorSampleRate: ${errorSampleRate} })`,
16306
14863
  this._replay.start();
16307
14864
  }
16308
14865
 
16309
- /**
16310
- * Start replay buffering. Buffers until `flush()` is called or, if
16311
- * `replaysOnErrorSampleRate` > 0, until an error occurs.
16312
- */
16313
- startBuffering() {
16314
- if (!this._replay) {
16315
- return;
16316
- }
16317
-
16318
- this._replay.startBuffering();
16319
- }
16320
-
16321
14866
  /**
16322
14867
  * Currently, this needs to be manually called (e.g. for tests). Sentry SDK
16323
14868
  * does not support a teardown
16324
14869
  */
16325
14870
  stop() {
16326
14871
  if (!this._replay) {
16327
- return Promise.resolve();
16328
- }
16329
-
16330
- return this._replay.stop();
16331
- }
16332
-
16333
- /**
16334
- * If not in "session" recording mode, flush event buffer which will create a new replay.
16335
- * Unless `continueRecording` is false, the replay will continue to record and
16336
- * behave as a "session"-based replay.
16337
- *
16338
- * Otherwise, queue up a flush.
16339
- */
16340
- flush(options) {
16341
- if (!this._replay || !this._replay.isEnabled()) {
16342
- return Promise.resolve();
14872
+ return;
16343
14873
  }
16344
14874
 
16345
- return this._replay.sendBufferedReplayOrFlush(options);
14875
+ this._replay.stop();
16346
14876
  }
16347
14877
 
16348
14878
  /**
16349
- * Get the current session ID.
14879
+ * Immediately send all pending events.
16350
14880
  */
16351
- getReplayId() {
14881
+ flush() {
16352
14882
  if (!this._replay || !this._replay.isEnabled()) {
16353
14883
  return;
16354
14884
  }
16355
14885
 
16356
- return this._replay.getSessionId();
16357
- }
16358
- /**
16359
- * Initializes replay.
16360
- */
16361
- _initialize() {
16362
- if (!this._replay) {
16363
- return;
16364
- }
16365
-
16366
- this._replay.initializeSampling();
14886
+ return this._replay.flushImmediate();
16367
14887
  }
16368
14888
 
16369
14889
  /** Setup the integration. */
@@ -16414,10 +14934,6 @@ function loadReplayOptionsFromClient(initialOptions) {
16414
14934
  return finalOptions;
16415
14935
  }
16416
14936
 
16417
- function _getMergedNetworkHeaders(headers) {
16418
- return [...DEFAULT_NETWORK_HEADERS, ...headers.map(header => header.toLowerCase())];
16419
- }
16420
-
16421
14937
  /**
16422
14938
  * Polyfill for the optional chain operator, `?.`, given previous conversion of the expression into an array of values,
16423
14939
  * descriptors, and functions.
@@ -16855,9 +15371,6 @@ class Postgres {
16855
15371
  const span = _optionalChain([parentSpan, 'optionalAccess', _6 => _6.startChild, 'call', _7 => _7({
16856
15372
  description: typeof config === 'string' ? config : (config ).text,
16857
15373
  op: 'db',
16858
- data: {
16859
- 'db.system': 'postgresql',
16860
- },
16861
15374
  })]);
16862
15375
 
16863
15376
  if (typeof callback === 'function') {
@@ -16934,9 +15447,6 @@ class Mysql {constructor() { Mysql.prototype.__init.call(this); }
16934
15447
  const span = _optionalChain([parentSpan, 'optionalAccess', _4 => _4.startChild, 'call', _5 => _5({
16935
15448
  description: typeof options === 'string' ? options : (options ).sql,
16936
15449
  op: 'db',
16937
- data: {
16938
- 'db.system': 'mysql',
16939
- },
16940
15450
  })]);
16941
15451
 
16942
15452
  if (typeof callback === 'function') {
@@ -17158,7 +15668,6 @@ class Mongo {
17158
15668
  collectionName: collection.collectionName,
17159
15669
  dbName: collection.dbName,
17160
15670
  namespace: collection.namespace,
17161
- 'db.system': 'mongodb',
17162
15671
  };
17163
15672
  const spanContext = {
17164
15673
  op: 'db',
@@ -17245,15 +15754,31 @@ class Prisma {
17245
15754
  }
17246
15755
 
17247
15756
  this._client.$use((params, next) => {
15757
+ const scope = getCurrentHub().getScope();
15758
+ const parentSpan = _optionalChain([scope, 'optionalAccess', _2 => _2.getSpan, 'call', _3 => _3()]);
15759
+
17248
15760
  const action = params.action;
17249
15761
  const model = params.model;
17250
- return trace(
17251
- { name: model ? `${model} ${action}` : action, op: 'db.sql.prisma', data: { 'db.system': 'prisma' } },
17252
- () => next(params),
17253
- );
15762
+
15763
+ const span = _optionalChain([parentSpan, 'optionalAccess', _4 => _4.startChild, 'call', _5 => _5({
15764
+ description: model ? `${model} ${action}` : action,
15765
+ op: 'db.sql.prisma',
15766
+ })]);
15767
+
15768
+ const rv = next(params);
15769
+
15770
+ if (isThenable(rv)) {
15771
+ return rv.then((res) => {
15772
+ _optionalChain([span, 'optionalAccess', _6 => _6.finish, 'call', _7 => _7()]);
15773
+ return res;
15774
+ });
15775
+ }
15776
+
15777
+ _optionalChain([span, 'optionalAccess', _8 => _8.finish, 'call', _9 => _9()]);
15778
+ return rv;
17254
15779
  });
17255
15780
  }
17256
- } Prisma.__initStatic();
15781
+ }Prisma.__initStatic();
17257
15782
 
17258
15783
  /** Tracing integration for graphql package */
17259
15784
  class GraphQL {constructor() { GraphQL.prototype.__init.call(this); }
@@ -30427,7 +28952,7 @@ const configGenerator = () => {
30427
28952
  let release;
30428
28953
  try {
30429
28954
  environment !== null && environment !== void 0 ? environment : (environment = "staging");
30430
- release !== null && release !== void 0 ? release : (release = "1.1.23-binary-004");
28955
+ release !== null && release !== void 0 ? release : (release = "1.1.23-binary-006");
30431
28956
  }
30432
28957
  catch (_a) {
30433
28958
  console.error('sentry configGenerator error');
@@ -43494,9 +42019,7 @@ var AsapAction = (function (_super) {
43494
42019
  var actions = scheduler.actions;
43495
42020
  if (id != null && ((_a = actions[actions.length - 1]) === null || _a === void 0 ? void 0 : _a.id) !== id) {
43496
42021
  immediateProvider.clearImmediate(id);
43497
- if (scheduler._scheduled === id) {
43498
- scheduler._scheduled = undefined;
43499
- }
42022
+ scheduler._scheduled = undefined;
43500
42023
  }
43501
42024
  return undefined;
43502
42025
  };