@sentry/replay 7.81.1 → 7.82.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.
Files changed (31) hide show
  1. package/cjs/index.js +133 -36
  2. package/cjs/index.js.map +1 -1
  3. package/esm/index.js +135 -38
  4. package/esm/index.js.map +1 -1
  5. package/package.json +6 -6
  6. package/types/coreHandlers/handleDom.d.ts +3 -4
  7. package/types/coreHandlers/handleDom.d.ts.map +1 -1
  8. package/types/coreHandlers/handleFetch.d.ts +1 -1
  9. package/types/coreHandlers/handleHistory.d.ts +3 -7
  10. package/types/coreHandlers/handleHistory.d.ts.map +1 -1
  11. package/types/coreHandlers/handleXhr.d.ts +1 -1
  12. package/types/coreHandlers/util/domUtils.d.ts +2 -7
  13. package/types/coreHandlers/util/domUtils.d.ts.map +1 -1
  14. package/types/coreHandlers/util/fetchUtils.d.ts +5 -1
  15. package/types/coreHandlers/util/fetchUtils.d.ts.map +1 -1
  16. package/types/coreHandlers/util/networkUtils.d.ts +4 -4
  17. package/types/coreHandlers/util/networkUtils.d.ts.map +1 -1
  18. package/types/coreHandlers/util/xhrUtils.d.ts.map +1 -1
  19. package/types/types/performance.d.ts +1 -1
  20. package/types/types/performance.d.ts.map +1 -1
  21. package/types/types/request.d.ts +1 -1
  22. package/types/types/request.d.ts.map +1 -1
  23. package/types-ts3.8/coreHandlers/handleDom.d.ts +3 -4
  24. package/types-ts3.8/coreHandlers/handleFetch.d.ts +1 -1
  25. package/types-ts3.8/coreHandlers/handleHistory.d.ts +3 -7
  26. package/types-ts3.8/coreHandlers/handleXhr.d.ts +1 -1
  27. package/types-ts3.8/coreHandlers/util/domUtils.d.ts +2 -7
  28. package/types-ts3.8/coreHandlers/util/fetchUtils.d.ts +5 -1
  29. package/types-ts3.8/coreHandlers/util/networkUtils.d.ts +7 -4
  30. package/types-ts3.8/types/performance.d.ts +1 -1
  31. package/types-ts3.8/types/request.d.ts +1 -1
package/esm/index.js CHANGED
@@ -1,5 +1,5 @@
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';
1
+ import { getCurrentHub, getClient, isSentryRequestUrl, addGlobalEventProcessor, prepareEvent, setContext, captureException } from '@sentry/core';
2
+ import { GLOBAL_OBJ, normalize, fill, htmlTreeAsString, browserPerformanceTimeOrigin, logger, uuid4, SENTRY_XHR_DATA_KEY, dropUndefinedKeys, stringMatchesSomePattern, addFetchInstrumentationHandler, addXhrInstrumentationHandler, addClickKeypressInstrumentationHandler, addHistoryInstrumentationHandler, 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`
@@ -4339,12 +4339,13 @@ const handleDomListener = (
4339
4339
  }
4340
4340
 
4341
4341
  const isClick = handlerData.name === 'click';
4342
- const event = isClick && (handlerData.event );
4342
+ const event = isClick ? (handlerData.event ) : undefined;
4343
4343
  // Ignore clicks if ctrl/alt/meta/shift keys are held down as they alter behavior of clicks (e.g. open in new tab)
4344
4344
  if (
4345
4345
  isClick &&
4346
4346
  replay.clickDetector &&
4347
4347
  event &&
4348
+ event.target &&
4348
4349
  !event.altKey &&
4349
4350
  !event.metaKey &&
4350
4351
  !event.ctrlKey &&
@@ -5470,7 +5471,7 @@ async function _addEvent(
5470
5471
  (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.error(error);
5471
5472
  await replay.stop({ reason });
5472
5473
 
5473
- const client = getCurrentHub().getClient();
5474
+ const client = getClient();
5474
5475
 
5475
5476
  if (client) {
5476
5477
  client.recordDroppedEvent('internal_sdk_error', 'replay');
@@ -5616,7 +5617,7 @@ function handleErrorEvent(replay, event) {
5616
5617
  }
5617
5618
 
5618
5619
  function isBaseTransportSend() {
5619
- const client = getCurrentHub().getClient();
5620
+ const client = getClient();
5620
5621
  if (!client) {
5621
5622
  return false;
5622
5623
  }
@@ -5822,7 +5823,7 @@ function handleHistory(handlerData) {
5822
5823
  }
5823
5824
 
5824
5825
  /**
5825
- * Returns a listener to be added to `addInstrumentationHandler('history', listener)`.
5826
+ * Returns a listener to be added to `addHistoryInstrumentationHandler(listener)`.
5826
5827
  */
5827
5828
  function handleHistorySpanListener(replay) {
5828
5829
  return (handlerData) => {
@@ -5911,7 +5912,7 @@ function handleFetch(handlerData) {
5911
5912
  }
5912
5913
 
5913
5914
  /**
5914
- * Returns a listener to be added to `addInstrumentationHandler('fetch', listener)`.
5915
+ * Returns a listener to be added to `addFetchInstrumentationHandler(listener)`.
5915
5916
  */
5916
5917
  function handleFetchSpanListener(replay) {
5917
5918
  return (handlerData) => {
@@ -5955,7 +5956,7 @@ function handleXhr(handlerData) {
5955
5956
  }
5956
5957
 
5957
5958
  /**
5958
- * Returns a listener to be added to `addInstrumentationHandler('xhr', listener)`.
5959
+ * Returns a listener to be added to `addXhrInstrumentationHandler(listener)`.
5959
5960
  */
5960
5961
  function handleXhrSpanListener(replay) {
5961
5962
  return (handlerData) => {
@@ -6022,23 +6023,47 @@ function parseContentLengthHeader(header) {
6022
6023
  function getBodyString(body) {
6023
6024
  try {
6024
6025
  if (typeof body === 'string') {
6025
- return body;
6026
+ return [body];
6026
6027
  }
6027
6028
 
6028
6029
  if (body instanceof URLSearchParams) {
6029
- return body.toString();
6030
+ return [body.toString()];
6030
6031
  }
6031
6032
 
6032
6033
  if (body instanceof FormData) {
6033
- return _serializeFormData(body);
6034
+ return [_serializeFormData(body)];
6034
6035
  }
6035
6036
  } catch (e2) {
6036
6037
  (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.warn('[Replay] Failed to serialize body', body);
6038
+ return [undefined, 'BODY_PARSE_ERROR'];
6037
6039
  }
6038
6040
 
6039
6041
  (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.info('[Replay] Skipping network body because of body type', body);
6040
6042
 
6041
- return undefined;
6043
+ return [undefined];
6044
+ }
6045
+
6046
+ /** Merge a warning into an existing network request/response. */
6047
+ function mergeWarning(
6048
+ info,
6049
+ warning,
6050
+ ) {
6051
+ if (!info) {
6052
+ return {
6053
+ headers: {},
6054
+ size: undefined,
6055
+ _meta: {
6056
+ warnings: [warning],
6057
+ },
6058
+ };
6059
+ }
6060
+
6061
+ const newMeta = { ...info._meta };
6062
+ const existingWarnings = newMeta.warnings || [];
6063
+ newMeta.warnings = [...existingWarnings, warning];
6064
+
6065
+ info._meta = newMeta;
6066
+ return info;
6042
6067
  }
6043
6068
 
6044
6069
  /** Convert ReplayNetworkRequestData to a PerformanceEntry. */
@@ -6171,7 +6196,7 @@ function normalizeNetworkBody(body)
6171
6196
  return {
6172
6197
  body: jsonBody,
6173
6198
  };
6174
- } catch (e4) {
6199
+ } catch (e3) {
6175
6200
  // fall back to just send the body as string
6176
6201
  }
6177
6202
  }
@@ -6316,10 +6341,17 @@ function _getRequestInfo(
6316
6341
 
6317
6342
  // We only want to transmit string or string-like bodies
6318
6343
  const requestBody = _getFetchRequestArgBody(input);
6319
- const bodyStr = getBodyString(requestBody);
6320
- return buildNetworkRequestOrResponse(headers, requestBodySize, bodyStr);
6344
+ const [bodyStr, warning] = getBodyString(requestBody);
6345
+ const data = buildNetworkRequestOrResponse(headers, requestBodySize, bodyStr);
6346
+
6347
+ if (warning) {
6348
+ return mergeWarning(data, warning);
6349
+ }
6350
+
6351
+ return data;
6321
6352
  }
6322
6353
 
6354
+ /** Exported only for tests. */
6323
6355
  async function _getResponseInfo(
6324
6356
  captureDetails,
6325
6357
  {
@@ -6342,12 +6374,35 @@ async function _getResponseInfo(
6342
6374
  return buildNetworkRequestOrResponse(headers, responseBodySize, undefined);
6343
6375
  }
6344
6376
 
6345
- // Only clone the response if we need to
6346
- try {
6347
- // We have to clone this, as the body can only be read once
6348
- const res = response.clone();
6349
- const bodyText = await _parseFetchBody(res);
6377
+ const [bodyText, warning] = await _parseFetchResponseBody(response);
6378
+ const result = getResponseData(bodyText, {
6379
+ networkCaptureBodies,
6380
+ textEncoder,
6381
+ responseBodySize,
6382
+ captureDetails,
6383
+ headers,
6384
+ });
6385
+
6386
+ if (warning) {
6387
+ return mergeWarning(result, warning);
6388
+ }
6389
+
6390
+ return result;
6391
+ }
6392
+
6393
+ function getResponseData(
6394
+ bodyText,
6395
+ {
6396
+ networkCaptureBodies,
6397
+ textEncoder,
6398
+ responseBodySize,
6399
+ captureDetails,
6400
+ headers,
6401
+ }
6350
6402
 
6403
+ ,
6404
+ ) {
6405
+ try {
6351
6406
  const size =
6352
6407
  bodyText && bodyText.length && responseBodySize === undefined
6353
6408
  ? getBodySize(bodyText, textEncoder)
@@ -6369,11 +6424,19 @@ async function _getResponseInfo(
6369
6424
  }
6370
6425
  }
6371
6426
 
6372
- async function _parseFetchBody(response) {
6427
+ async function _parseFetchResponseBody(response) {
6428
+ const res = _tryCloneResponse(response);
6429
+
6430
+ if (!res) {
6431
+ return [undefined, 'BODY_PARSE_ERROR'];
6432
+ }
6433
+
6373
6434
  try {
6374
- return await response.text();
6375
- } catch (e) {
6376
- return undefined;
6435
+ const text = await _tryGetResponseText(res);
6436
+ return [text];
6437
+ } catch (error) {
6438
+ (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.warn('[Replay] Failed to get text body from response', error);
6439
+ return [undefined, 'BODY_PARSE_ERROR'];
6377
6440
  }
6378
6441
  }
6379
6442
 
@@ -6436,6 +6499,40 @@ function getHeadersFromOptions(
6436
6499
  return getAllowedHeaders(headers, allowedHeaders);
6437
6500
  }
6438
6501
 
6502
+ function _tryCloneResponse(response) {
6503
+ try {
6504
+ // We have to clone this, as the body can only be read once
6505
+ return response.clone();
6506
+ } catch (error) {
6507
+ // this can throw if the response was already consumed before
6508
+ (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.warn('[Replay] Failed to clone response body', error);
6509
+ }
6510
+ }
6511
+
6512
+ /**
6513
+ * Get the response body of a fetch request, or timeout after 500ms.
6514
+ * Fetch can return a streaming body, that may not resolve (or not for a long time).
6515
+ * If that happens, we rather abort after a short time than keep waiting for this.
6516
+ */
6517
+ function _tryGetResponseText(response) {
6518
+ return new Promise((resolve, reject) => {
6519
+ const timeout = setTimeout(() => reject(new Error('Timeout while trying to read response body')), 500);
6520
+
6521
+ _getResponseText(response)
6522
+ .then(
6523
+ txt => resolve(txt),
6524
+ reason => reject(reason),
6525
+ )
6526
+ .finally(() => clearTimeout(timeout));
6527
+ });
6528
+ }
6529
+
6530
+ async function _getResponseText(response) {
6531
+ // Force this to be a promise, just to be safe
6532
+ // eslint-disable-next-line no-return-await
6533
+ return await response.text();
6534
+ }
6535
+
6439
6536
  /**
6440
6537
  * Capture an XHR breadcrumb to a replay.
6441
6538
  * This adds additional data (where approriate).
@@ -6525,8 +6622,8 @@ function _prepareXhrData(
6525
6622
  : {};
6526
6623
  const networkResponseHeaders = getAllowedHeaders(getResponseHeaders(xhr), options.networkResponseHeaders);
6527
6624
 
6528
- const requestBody = options.networkCaptureBodies ? getBodyString(input) : undefined;
6529
- const responseBody = options.networkCaptureBodies ? _getXhrResponseBody(xhr) : undefined;
6625
+ const [requestBody, requestWarning] = options.networkCaptureBodies ? getBodyString(input) : [undefined];
6626
+ const [responseBody, responseWarning] = options.networkCaptureBodies ? _getXhrResponseBody(xhr) : [undefined];
6530
6627
 
6531
6628
  const request = buildNetworkRequestOrResponse(networkRequestHeaders, requestBodySize, requestBody);
6532
6629
  const response = buildNetworkRequestOrResponse(networkResponseHeaders, responseBodySize, responseBody);
@@ -6537,8 +6634,8 @@ function _prepareXhrData(
6537
6634
  url,
6538
6635
  method,
6539
6636
  statusCode,
6540
- request,
6541
- response,
6637
+ request: requestWarning ? mergeWarning(request, requestWarning) : request,
6638
+ response: responseWarning ? mergeWarning(response, responseWarning) : response,
6542
6639
  };
6543
6640
  }
6544
6641
 
@@ -6561,7 +6658,7 @@ function _getXhrResponseBody(xhr) {
6561
6658
  const errors = [];
6562
6659
 
6563
6660
  try {
6564
- return xhr.responseText;
6661
+ return [xhr.responseText];
6565
6662
  } catch (e) {
6566
6663
  errors.push(e);
6567
6664
  }
@@ -6576,7 +6673,7 @@ function _getXhrResponseBody(xhr) {
6576
6673
 
6577
6674
  (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.warn('[Replay] Failed to get xhr response body', ...errors);
6578
6675
 
6579
- return undefined;
6676
+ return [undefined];
6580
6677
  }
6581
6678
 
6582
6679
  /**
@@ -6586,7 +6683,7 @@ function _getXhrResponseBody(xhr) {
6586
6683
  * (enriching it with further data that is _not_ added to the regular breadcrumbs)
6587
6684
  */
6588
6685
  function handleNetworkBreadcrumbs(replay) {
6589
- const client = getCurrentHub().getClient();
6686
+ const client = getClient();
6590
6687
 
6591
6688
  try {
6592
6689
  const textEncoder = new TextEncoder();
@@ -6613,8 +6710,8 @@ function handleNetworkBreadcrumbs(replay) {
6613
6710
  client.on('beforeAddBreadcrumb', (breadcrumb, hint) => beforeAddNetworkBreadcrumb(options, breadcrumb, hint));
6614
6711
  } else {
6615
6712
  // Fallback behavior
6616
- addInstrumentationHandler('fetch', handleFetchSpanListener(replay));
6617
- addInstrumentationHandler('xhr', handleXhrSpanListener(replay));
6713
+ addFetchInstrumentationHandler(handleFetchSpanListener(replay));
6714
+ addXhrInstrumentationHandler(handleXhrSpanListener(replay));
6618
6715
  }
6619
6716
  } catch (e2) {
6620
6717
  // Do nothing
@@ -6784,11 +6881,11 @@ function normalizeConsoleBreadcrumb(
6784
6881
  function addGlobalListeners(replay) {
6785
6882
  // Listeners from core SDK //
6786
6883
  const scope = getCurrentHub().getScope();
6787
- const client = getCurrentHub().getClient();
6884
+ const client = getClient();
6788
6885
 
6789
6886
  scope.addScopeListener(handleScopeListener(replay));
6790
- addInstrumentationHandler('dom', handleDomListener(replay));
6791
- addInstrumentationHandler('history', handleHistorySpanListener(replay));
6887
+ addClickKeypressInstrumentationHandler(handleDomListener(replay));
6888
+ addHistoryInstrumentationHandler(handleHistorySpanListener(replay));
6792
6889
  handleNetworkBreadcrumbs(replay);
6793
6890
 
6794
6891
  // Tag all (non replay) events that get sent to Sentry with the current
@@ -8486,7 +8583,7 @@ class ReplayContainer {
8486
8583
  // In this case, we want to completely stop the replay - otherwise, we may get inconsistent segments
8487
8584
  void this.stop({ reason: 'sendReplay' });
8488
8585
 
8489
- const client = getCurrentHub().getClient();
8586
+ const client = getClient();
8490
8587
 
8491
8588
  if (client) {
8492
8589
  client.recordDroppedEvent('send_error', 'replay');
@@ -9051,7 +9148,7 @@ Sentry.init({ replaysOnErrorSampleRate: ${errorSampleRate} })`,
9051
9148
 
9052
9149
  /** Parse Replay-related options from SDK options */
9053
9150
  function loadReplayOptionsFromClient(initialOptions) {
9054
- const client = getCurrentHub().getClient();
9151
+ const client = getClient();
9055
9152
  const opt = client && (client.getOptions() );
9056
9153
 
9057
9154
  const finalOptions = { sessionSampleRate: 0, errorSampleRate: 0, ...dropUndefinedKeys(initialOptions) };