@sentry/replay 7.86.0 → 7.88.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
@@ -2943,6 +2943,10 @@ function initObservers(o, _hooks = {}) {
2943
2943
  };
2944
2944
  const selectionObserver = initSelectionObserver(o);
2945
2945
  const customElementObserver = initCustomElementObserver(o);
2946
+ const pluginHandlers = [];
2947
+ for (const plugin of o.plugins) {
2948
+ pluginHandlers.push(plugin.observer(plugin.callback, currentWindow, plugin.options));
2949
+ }
2946
2950
  return callbackWrapper(() => {
2947
2951
  mutationBuffers.forEach((b) => b.reset());
2948
2952
  mutationObserver.disconnect();
@@ -2958,6 +2962,7 @@ function initObservers(o, _hooks = {}) {
2958
2962
  fontObserver();
2959
2963
  selectionObserver();
2960
2964
  customElementObserver();
2965
+ pluginHandlers.forEach((h) => h());
2961
2966
  });
2962
2967
  }
2963
2968
  function hasNestedCSSRule(prop) {
@@ -3437,12 +3442,13 @@ function wrapEvent(e) {
3437
3442
  eWithTime.timestamp = nowTimestamp();
3438
3443
  return eWithTime;
3439
3444
  }
3445
+ let _wrappedEmit;
3440
3446
  let _takeFullSnapshot;
3441
3447
  const mirror = createMirror();
3442
3448
  function record(options = {}) {
3443
3449
  const { emit, checkoutEveryNms, checkoutEveryNth, blockClass = 'rr-block', blockSelector = null, unblockSelector = null, ignoreClass = 'rr-ignore', ignoreSelector = null, maskAllText = false, maskTextClass = 'rr-mask', unmaskTextClass = null, maskTextSelector = null, unmaskTextSelector = null, inlineStylesheet = true, maskAllInputs, maskInputOptions: _maskInputOptions, slimDOMOptions: _slimDOMOptions, maskAttributeFn, maskInputFn, maskTextFn, packFn, sampling = {}, dataURLOptions = {}, mousemoveWait, recordCanvas = false, recordCrossOriginIframes = false, recordAfter = options.recordAfter === 'DOMContentLoaded'
3444
3450
  ? options.recordAfter
3445
- : 'load', userTriggeredOnInput = false, collectFonts = false, inlineImages = false, keepIframeSrcFn = () => false, ignoreCSSAttributes = new Set([]), errorHandler, onMutation, getCanvasManager, } = options;
3451
+ : 'load', userTriggeredOnInput = false, collectFonts = false, inlineImages = false, plugins, keepIframeSrcFn = () => false, ignoreCSSAttributes = new Set([]), errorHandler, onMutation, getCanvasManager, } = options;
3446
3452
  registerErrorHandler(errorHandler);
3447
3453
  const inEmittingFrame = recordCrossOriginIframes
3448
3454
  ? window.parent === window
@@ -3508,6 +3514,11 @@ function record(options = {}) {
3508
3514
  let lastFullSnapshotEvent;
3509
3515
  let incrementalSnapshotCount = 0;
3510
3516
  const eventProcessor = (e) => {
3517
+ for (const plugin of plugins || []) {
3518
+ if (plugin.eventProcessor) {
3519
+ e = plugin.eventProcessor(e);
3520
+ }
3521
+ }
3511
3522
  if (packFn &&
3512
3523
  !passEmitsToParent) {
3513
3524
  e = packFn(e);
@@ -3552,6 +3563,7 @@ function record(options = {}) {
3552
3563
  }
3553
3564
  }
3554
3565
  };
3566
+ _wrappedEmit = wrappedEmit;
3555
3567
  const wrappedMutationEmit = (m) => {
3556
3568
  wrappedEmit(wrapEvent({
3557
3569
  type: EventType.IncrementalSnapshot,
@@ -3583,17 +3595,23 @@ function record(options = {}) {
3583
3595
  recordCrossOriginIframes,
3584
3596
  wrappedEmit,
3585
3597
  });
3598
+ for (const plugin of plugins || []) {
3599
+ if (plugin.getMirror)
3600
+ plugin.getMirror({
3601
+ nodeMirror: mirror,
3602
+ crossOriginIframeMirror: iframeManager.crossOriginIframeMirror,
3603
+ crossOriginIframeStyleMirror: iframeManager.crossOriginIframeStyleMirror,
3604
+ });
3605
+ }
3586
3606
  const processedNodeManager = new ProcessedNodeManager();
3587
- const canvasManager = getCanvasManager
3588
- ? getCanvasManager({
3589
- recordCanvas,
3590
- blockClass,
3591
- blockSelector,
3592
- unblockSelector,
3593
- sampling: sampling['canvas'],
3594
- dataURLOptions,
3595
- })
3596
- : new CanvasManagerNoop();
3607
+ const canvasManager = _getCanvasManager(getCanvasManager, {
3608
+ recordCanvas,
3609
+ blockClass,
3610
+ blockSelector,
3611
+ unblockSelector,
3612
+ sampling: sampling['canvas'],
3613
+ dataURLOptions,
3614
+ });
3597
3615
  const shadowDomManager = typeof __RRWEB_EXCLUDE_SHADOW_DOM__ === 'boolean' &&
3598
3616
  __RRWEB_EXCLUDE_SHADOW_DOM__
3599
3617
  ? new ShadowDomManagerNoop()
@@ -3688,7 +3706,7 @@ function record(options = {}) {
3688
3706
  node,
3689
3707
  initialOffset: getWindowScroll(window),
3690
3708
  },
3691
- }), isCheckout);
3709
+ }));
3692
3710
  mutationBuffers.forEach((buf) => buf.unlock());
3693
3711
  if (document.adoptedStyleSheets && document.adoptedStyleSheets.length > 0)
3694
3712
  stylesheetManager.adoptStyleSheets(document.adoptedStyleSheets, mirror.getId(document));
@@ -3697,6 +3715,7 @@ function record(options = {}) {
3697
3715
  try {
3698
3716
  const handlers = [];
3699
3717
  const observe = (doc) => {
3718
+ var _a;
3700
3719
  return callbackWrapper(initObservers)({
3701
3720
  onMutation,
3702
3721
  mutationCb: wrappedMutationEmit,
@@ -3780,7 +3799,17 @@ function record(options = {}) {
3780
3799
  processedNodeManager,
3781
3800
  canvasManager,
3782
3801
  ignoreCSSAttributes,
3783
- plugins: [],
3802
+ plugins: ((_a = plugins === null || plugins === void 0 ? void 0 : plugins.filter((p) => p.observer)) === null || _a === void 0 ? void 0 : _a.map((p) => ({
3803
+ observer: p.observer,
3804
+ options: p.options,
3805
+ callback: (payload) => wrappedEmit(wrapEvent({
3806
+ type: EventType.Plugin,
3807
+ data: {
3808
+ plugin: p.name,
3809
+ payload,
3810
+ },
3811
+ })),
3812
+ }))) || [],
3784
3813
  }, {});
3785
3814
  };
3786
3815
  iframeManager.addLoadListener((iframeEl) => {
@@ -3834,8 +3863,22 @@ function takeFullSnapshot(isCheckout) {
3834
3863
  }
3835
3864
  _takeFullSnapshot(isCheckout);
3836
3865
  }
3866
+ function wrappedEmit(e) {
3867
+ if (!_wrappedEmit) {
3868
+ return;
3869
+ }
3870
+ _wrappedEmit(e);
3871
+ }
3837
3872
  record.mirror = mirror;
3838
3873
  record.takeFullSnapshot = takeFullSnapshot;
3874
+ function _getCanvasManager(getCanvasManagerFn, options) {
3875
+ return getCanvasManagerFn
3876
+ ? getCanvasManagerFn(Object.assign(Object.assign({}, options), { mirror, win: window, mutationCb: (p) => wrappedEmit(wrapEvent({
3877
+ type: EventType.IncrementalSnapshot,
3878
+ data: Object.assign({ source: IncrementalSource.CanvasMutation }, p),
3879
+ })) }))
3880
+ : new CanvasManagerNoop();
3881
+ }
3839
3882
 
3840
3883
  const ReplayEventTypeIncrementalSnapshot = 3;
3841
3884
  const ReplayEventTypeCustom = 5;
@@ -5639,6 +5682,40 @@ function isBaseTransportSend() {
5639
5682
  );
5640
5683
  }
5641
5684
 
5685
+ /**
5686
+ * Returns a listener to be added to `client.on('afterSendErrorEvent, listener)`.
5687
+ */
5688
+ function handleBeforeSendEvent(replay) {
5689
+ return (event) => {
5690
+ if (!replay.isEnabled() || !isErrorEvent(event)) {
5691
+ return;
5692
+ }
5693
+
5694
+ handleHydrationError(replay, event);
5695
+ };
5696
+ }
5697
+
5698
+ function handleHydrationError(replay, event) {
5699
+ const exceptionValue = event.exception && event.exception.values && event.exception.values[0].value;
5700
+ if (typeof exceptionValue !== 'string') {
5701
+ return;
5702
+ }
5703
+
5704
+ if (
5705
+ // Only matches errors in production builds of react-dom
5706
+ // Example https://reactjs.org/docs/error-decoder.html?invariant=423
5707
+ exceptionValue.match(/reactjs\.org\/docs\/error-decoder\.html\?invariant=(418|419|422|423|425)/) ||
5708
+ // Development builds of react-dom
5709
+ // Example Text: content did not match. Server: "A" Client: "B"
5710
+ exceptionValue.match(/(hydration|content does not match|did not match)/i)
5711
+ ) {
5712
+ const breadcrumb = createBreadcrumb({
5713
+ category: 'replay.hydrate-error',
5714
+ });
5715
+ addBreadcrumbEvent(replay, breadcrumb);
5716
+ }
5717
+ }
5718
+
5642
5719
  /**
5643
5720
  * Returns true if we think the given event is an error originating inside of rrweb.
5644
5721
  */
@@ -6963,6 +7040,7 @@ function addGlobalListeners(replay) {
6963
7040
 
6964
7041
  // If a custom client has no hooks yet, we continue to use the "old" implementation
6965
7042
  if (hasHooks(client)) {
7043
+ client.on('beforeSendEvent', handleBeforeSendEvent(replay));
6966
7044
  client.on('afterSendEvent', handleAfterSendEvent(replay));
6967
7045
  client.on('createDsc', (dsc) => {
6968
7046
  const replayId = replay.getSessionId();
@@ -7870,6 +7948,7 @@ class ReplayContainer {
7870
7948
  */
7871
7949
  startRecording() {
7872
7950
  try {
7951
+ const canvas = this._options._experiments.canvas;
7873
7952
  this._stopRecording = record({
7874
7953
  ...this._recordingOptions,
7875
7954
  // When running in error sampling mode, we need to overwrite `checkoutEveryNms`
@@ -7878,6 +7957,12 @@ class ReplayContainer {
7878
7957
  ...(this.recordingMode === 'buffer' && { checkoutEveryNms: BUFFER_CHECKOUT_TIME }),
7879
7958
  emit: getHandleRecordingEmit(this),
7880
7959
  onMutation: this._onMutationHandler,
7960
+ ...(canvas && {
7961
+ recordCanvas: true,
7962
+ sampling: { canvas: canvas.fps || 4 },
7963
+ dataURLOptions: { quality: canvas.quality || 0.6 },
7964
+ getCanvasManager: canvas.manager,
7965
+ }),
7881
7966
  });
7882
7967
  } catch (err) {
7883
7968
  this._handleException(err);