@sentry/replay 7.80.0 → 7.80.2-alpha.0

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/esm/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { getCurrentHub, isSentryRequestUrl, addGlobalEventProcessor, prepareEvent, setContext, captureException } from '@sentry/core';
2
- import { GLOBAL_OBJ, normalize, fill, htmlTreeAsString, browserPerformanceTimeOrigin, logger, uuid4, SENTRY_XHR_DATA_KEY, dropUndefinedKeys, stringMatchesSomePattern, addInstrumentationHandler, createEnvelope, createEventEnvelopeHeaders, getSdkMetadataForEnvelopeHeader, updateRateLimits, isRateLimited, isBrowser } from '@sentry/utils';
2
+ import { GLOBAL_OBJ, normalize, fill, getElementIdentifier, browserPerformanceTimeOrigin, logger, uuid4, SENTRY_XHR_DATA_KEY, dropUndefinedKeys, stringMatchesSomePattern, addInstrumentationHandler, createEnvelope, createEventEnvelopeHeaders, getSdkMetadataForEnvelopeHeader, updateRateLimits, isRateLimited, isBrowser } from '@sentry/utils';
3
3
  import { addPerformanceInstrumentationHandler, addLcpInstrumentationHandler } from '@sentry-internal/tracing';
4
4
 
5
5
  // exporting a separate copy of `WINDOW` rather than exporting the one from `@sentry/browser`
@@ -4410,7 +4410,7 @@ function getDomTarget(handlerData) {
4410
4410
  // Accessing event.target can throw (see getsentry/raven-js#838, #768)
4411
4411
  try {
4412
4412
  target = isClick ? getClickTargetNode(handlerData.event) : getTargetNode(handlerData.event);
4413
- message = htmlTreeAsString(target, { maxStringLength: 200 }) || '<unknown>';
4413
+ message = getElementIdentifier(target, { maxStringLength: 200 }) || '<unknown>';
4414
4414
  } catch (e) {
4415
4415
  message = '<unknown>';
4416
4416
  }
@@ -4461,7 +4461,7 @@ function getKeyboardBreadcrumb(event) {
4461
4461
  return null;
4462
4462
  }
4463
4463
 
4464
- const message = htmlTreeAsString(target, { maxStringLength: 200 }) || '<unknown>';
4464
+ const message = getElementIdentifier(target, { maxStringLength: 200 }) || '<unknown>';
4465
4465
  const baseBreadcrumb = getBaseDomBreadcrumb(target , message);
4466
4466
 
4467
4467
  return createBreadcrumb({
@@ -5538,6 +5538,11 @@ function isReplayEvent(event) {
5538
5538
  return event.type === 'replay_event';
5539
5539
  }
5540
5540
 
5541
+ /** If the event is a feedback event */
5542
+ function isFeedbackEvent(event) {
5543
+ return event.type === 'feedback';
5544
+ }
5545
+
5541
5546
  /**
5542
5547
  * Returns a listener to be added to `client.on('afterSendErrorEvent, listener)`.
5543
5548
  */
@@ -5642,6 +5647,37 @@ function isRrwebError(event, hint) {
5642
5647
  return false;
5643
5648
  }
5644
5649
 
5650
+ /**
5651
+ * Add a feedback breadcrumb event to replay.
5652
+ */
5653
+ function addFeedbackBreadcrumb(replay, event) {
5654
+ replay.triggerUserActivity();
5655
+ replay.addUpdate(() => {
5656
+ if (!event.timestamp) {
5657
+ // Ignore events that don't have timestamps (this shouldn't happen, more of a typing issue)
5658
+ // Return true here so that we don't flush
5659
+ return true;
5660
+ }
5661
+
5662
+ void replay.throttledAddEvent({
5663
+ type: EventType.Custom,
5664
+ timestamp: event.timestamp * 1000,
5665
+ data: {
5666
+ timestamp: event.timestamp,
5667
+ tag: 'breadcrumb',
5668
+ payload: {
5669
+ category: 'sentry.feedback',
5670
+ data: {
5671
+ feedbackId: event.event_id,
5672
+ },
5673
+ },
5674
+ },
5675
+ });
5676
+
5677
+ return false;
5678
+ });
5679
+ }
5680
+
5645
5681
  /**
5646
5682
  * Determine if event should be sampled (only applies in buffer mode).
5647
5683
  * When an event is captured by `hanldleGlobalEvent`, when in buffer mode
@@ -5689,8 +5725,8 @@ function handleGlobalEventListener(
5689
5725
  return event;
5690
5726
  }
5691
5727
 
5692
- // We only want to handle errors & transactions, nothing else
5693
- if (!isErrorEvent(event) && !isTransactionEvent(event)) {
5728
+ // We only want to handle errors, transactions, and feedbacks, nothing else
5729
+ if (!isErrorEvent(event) && !isTransactionEvent(event) && !isFeedbackEvent(event)) {
5694
5730
  return event;
5695
5731
  }
5696
5732
 
@@ -5700,6 +5736,12 @@ function handleGlobalEventListener(
5700
5736
  return event;
5701
5737
  }
5702
5738
 
5739
+ if (isFeedbackEvent(event)) {
5740
+ // Add a replay breadcrumb for this piece of feedback
5741
+ addFeedbackBreadcrumb(replay, event);
5742
+ return event;
5743
+ }
5744
+
5703
5745
  // Unless `captureExceptions` is enabled, we want to ignore errors coming from rrweb
5704
5746
  // As there can be a bunch of stuff going wrong in internals there, that we don't want to bubble up to users
5705
5747
  if (isRrwebError(event, hint) && !replay.getOptions()._experiments.captureExceptions) {
@@ -5976,16 +6018,20 @@ function parseContentLengthHeader(header) {
5976
6018
 
5977
6019
  /** Get the string representation of a body. */
5978
6020
  function getBodyString(body) {
5979
- if (typeof body === 'string') {
5980
- return body;
5981
- }
6021
+ try {
6022
+ if (typeof body === 'string') {
6023
+ return body;
6024
+ }
5982
6025
 
5983
- if (body instanceof URLSearchParams) {
5984
- return body.toString();
5985
- }
6026
+ if (body instanceof URLSearchParams) {
6027
+ return body.toString();
6028
+ }
5986
6029
 
5987
- if (body instanceof FormData) {
5988
- return _serializeFormData(body);
6030
+ if (body instanceof FormData) {
6031
+ return _serializeFormData(body);
6032
+ }
6033
+ } catch (e2) {
6034
+ (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.warn('[Replay] Failed to serialize body', body);
5989
6035
  }
5990
6036
 
5991
6037
  return undefined;
@@ -6097,7 +6143,6 @@ function normalizeNetworkBody(body)
6097
6143
  }
6098
6144
 
6099
6145
  const exceedsSizeLimit = body.length > NETWORK_BODY_MAX_SIZE;
6100
-
6101
6146
  const isProbablyJson = _strIsProbablyJson(body);
6102
6147
 
6103
6148
  if (exceedsSizeLimit) {
@@ -6122,7 +6167,7 @@ function normalizeNetworkBody(body)
6122
6167
  return {
6123
6168
  body: jsonBody,
6124
6169
  };
6125
- } catch (e3) {
6170
+ } catch (e4) {
6126
6171
  // fall back to just send the body as string
6127
6172
  }
6128
6173
  }
@@ -6204,7 +6249,7 @@ function enrichFetchBreadcrumb(
6204
6249
  ) {
6205
6250
  const { input, response } = hint;
6206
6251
 
6207
- const body = _getFetchRequestArgBody(input);
6252
+ const body = input ? _getFetchRequestArgBody(input) : undefined;
6208
6253
  const reqSize = getBodySize(body, options.textEncoder);
6209
6254
 
6210
6255
  const resSize = response ? parseContentLengthHeader(response.headers.get('content-length')) : undefined;
@@ -6224,7 +6269,8 @@ async function _prepareFetchData(
6224
6269
 
6225
6270
  ,
6226
6271
  ) {
6227
- const { startTimestamp, endTimestamp } = hint;
6272
+ const now = Date.now();
6273
+ const { startTimestamp = now, endTimestamp = now } = hint;
6228
6274
 
6229
6275
  const {
6230
6276
  url,
@@ -6258,7 +6304,7 @@ function _getRequestInfo(
6258
6304
  input,
6259
6305
  requestBodySize,
6260
6306
  ) {
6261
- const headers = getRequestHeaders(input, networkRequestHeaders);
6307
+ const headers = input ? getRequestHeaders(input, networkRequestHeaders) : {};
6262
6308
 
6263
6309
  if (!networkCaptureBodies) {
6264
6310
  return buildNetworkRequestOrResponse(headers, requestBodySize, undefined);
@@ -6286,9 +6332,9 @@ async function _getResponseInfo(
6286
6332
  return buildSkippedNetworkRequestOrResponse(responseBodySize);
6287
6333
  }
6288
6334
 
6289
- const headers = getAllHeaders(response.headers, networkResponseHeaders);
6335
+ const headers = response ? getAllHeaders(response.headers, networkResponseHeaders) : {};
6290
6336
 
6291
- if (!networkCaptureBodies && responseBodySize !== undefined) {
6337
+ if (!response || (!networkCaptureBodies && responseBodySize !== undefined)) {
6292
6338
  return buildNetworkRequestOrResponse(headers, responseBodySize, undefined);
6293
6339
  }
6294
6340
 
@@ -6312,7 +6358,8 @@ async function _getResponseInfo(
6312
6358
  }
6313
6359
 
6314
6360
  return buildNetworkRequestOrResponse(headers, size, undefined);
6315
- } catch (e) {
6361
+ } catch (error) {
6362
+ (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.warn('[Replay] Failed to serialize response body', error);
6316
6363
  // fallback
6317
6364
  return buildNetworkRequestOrResponse(headers, responseBodySize, undefined);
6318
6365
  }
@@ -6321,7 +6368,7 @@ async function _getResponseInfo(
6321
6368
  async function _parseFetchBody(response) {
6322
6369
  try {
6323
6370
  return await response.text();
6324
- } catch (e2) {
6371
+ } catch (e) {
6325
6372
  return undefined;
6326
6373
  }
6327
6374
  }
@@ -6417,6 +6464,10 @@ function enrichXhrBreadcrumb(
6417
6464
  ) {
6418
6465
  const { xhr, input } = hint;
6419
6466
 
6467
+ if (!xhr) {
6468
+ return;
6469
+ }
6470
+
6420
6471
  const reqSize = getBodySize(input, options.textEncoder);
6421
6472
  const resSize = xhr.getResponseHeader('content-length')
6422
6473
  ? parseContentLengthHeader(xhr.getResponseHeader('content-length'))
@@ -6435,7 +6486,8 @@ function _prepareXhrData(
6435
6486
  hint,
6436
6487
  options,
6437
6488
  ) {
6438
- const { startTimestamp, endTimestamp, input, xhr } = hint;
6489
+ const now = Date.now();
6490
+ const { startTimestamp = now, endTimestamp = now, input, xhr } = hint;
6439
6491
 
6440
6492
  const {
6441
6493
  url,
@@ -6449,7 +6501,7 @@ function _prepareXhrData(
6449
6501
  return null;
6450
6502
  }
6451
6503
 
6452
- if (!urlMatches(url, options.networkDetailAllowUrls) || urlMatches(url, options.networkDetailDenyUrls)) {
6504
+ if (!xhr || !urlMatches(url, options.networkDetailAllowUrls) || urlMatches(url, options.networkDetailDenyUrls)) {
6453
6505
  const request = buildSkippedNetworkRequestOrResponse(requestBodySize);
6454
6506
  const response = buildSkippedNetworkRequestOrResponse(responseBodySize);
6455
6507
  return {
@@ -6469,16 +6521,11 @@ function _prepareXhrData(
6469
6521
  : {};
6470
6522
  const networkResponseHeaders = getAllowedHeaders(getResponseHeaders(xhr), options.networkResponseHeaders);
6471
6523
 
6472
- const request = buildNetworkRequestOrResponse(
6473
- networkRequestHeaders,
6474
- requestBodySize,
6475
- options.networkCaptureBodies ? getBodyString(input) : undefined,
6476
- );
6477
- const response = buildNetworkRequestOrResponse(
6478
- networkResponseHeaders,
6479
- responseBodySize,
6480
- options.networkCaptureBodies ? hint.xhr.responseText : undefined,
6481
- );
6524
+ const requestBody = options.networkCaptureBodies ? getBodyString(input) : undefined;
6525
+ const responseBody = options.networkCaptureBodies ? _getXhrResponseBody(xhr) : undefined;
6526
+
6527
+ const request = buildNetworkRequestOrResponse(networkRequestHeaders, requestBodySize, requestBody);
6528
+ const response = buildNetworkRequestOrResponse(networkResponseHeaders, responseBodySize, responseBody);
6482
6529
 
6483
6530
  return {
6484
6531
  startTimestamp,
@@ -6505,6 +6552,20 @@ function getResponseHeaders(xhr) {
6505
6552
  }, {});
6506
6553
  }
6507
6554
 
6555
+ function _getXhrResponseBody(xhr) {
6556
+ try {
6557
+ return xhr.responseText;
6558
+ } catch (e) {} // eslint-disable-line no-empty
6559
+
6560
+ // Try to manually parse the response body, if responseText fails
6561
+ try {
6562
+ const response = xhr.response;
6563
+ return getBodyString(response);
6564
+ } catch (e2) {} // eslint-disable-line no-empty
6565
+
6566
+ return undefined;
6567
+ }
6568
+
6508
6569
  /**
6509
6570
  * This method does two things:
6510
6571
  * - It enriches the regular XHR/fetch breadcrumbs with request/response size data