@sentry/replay 7.85.0 → 7.87.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) {
@@ -3442,7 +3447,7 @@ const mirror = createMirror();
3442
3447
  function record(options = {}) {
3443
3448
  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
3449
  ? options.recordAfter
3445
- : 'load', userTriggeredOnInput = false, collectFonts = false, inlineImages = false, keepIframeSrcFn = () => false, ignoreCSSAttributes = new Set([]), errorHandler, onMutation, getCanvasManager, } = options;
3450
+ : 'load', userTriggeredOnInput = false, collectFonts = false, inlineImages = false, plugins, keepIframeSrcFn = () => false, ignoreCSSAttributes = new Set([]), errorHandler, onMutation, getCanvasManager, } = options;
3446
3451
  registerErrorHandler(errorHandler);
3447
3452
  const inEmittingFrame = recordCrossOriginIframes
3448
3453
  ? window.parent === window
@@ -3508,6 +3513,11 @@ function record(options = {}) {
3508
3513
  let lastFullSnapshotEvent;
3509
3514
  let incrementalSnapshotCount = 0;
3510
3515
  const eventProcessor = (e) => {
3516
+ for (const plugin of plugins || []) {
3517
+ if (plugin.eventProcessor) {
3518
+ e = plugin.eventProcessor(e);
3519
+ }
3520
+ }
3511
3521
  if (packFn &&
3512
3522
  !passEmitsToParent) {
3513
3523
  e = packFn(e);
@@ -3583,6 +3593,14 @@ function record(options = {}) {
3583
3593
  recordCrossOriginIframes,
3584
3594
  wrappedEmit,
3585
3595
  });
3596
+ for (const plugin of plugins || []) {
3597
+ if (plugin.getMirror)
3598
+ plugin.getMirror({
3599
+ nodeMirror: mirror,
3600
+ crossOriginIframeMirror: iframeManager.crossOriginIframeMirror,
3601
+ crossOriginIframeStyleMirror: iframeManager.crossOriginIframeStyleMirror,
3602
+ });
3603
+ }
3586
3604
  const processedNodeManager = new ProcessedNodeManager();
3587
3605
  const canvasManager = getCanvasManager
3588
3606
  ? getCanvasManager({
@@ -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) => {
@@ -5639,6 +5668,40 @@ function isBaseTransportSend() {
5639
5668
  );
5640
5669
  }
5641
5670
 
5671
+ /**
5672
+ * Returns a listener to be added to `client.on('afterSendErrorEvent, listener)`.
5673
+ */
5674
+ function handleBeforeSendEvent(replay) {
5675
+ return (event) => {
5676
+ if (!replay.isEnabled() || !isErrorEvent(event)) {
5677
+ return;
5678
+ }
5679
+
5680
+ handleHydrationError(replay, event);
5681
+ };
5682
+ }
5683
+
5684
+ function handleHydrationError(replay, event) {
5685
+ const exceptionValue = event.exception && event.exception.values && event.exception.values[0].value;
5686
+ if (typeof exceptionValue !== 'string') {
5687
+ return;
5688
+ }
5689
+
5690
+ if (
5691
+ // Only matches errors in production builds of react-dom
5692
+ // Example https://reactjs.org/docs/error-decoder.html?invariant=423
5693
+ exceptionValue.match(/reactjs\.org\/docs\/error-decoder\.html\?invariant=(418|419|422|423|425)/) ||
5694
+ // Development builds of react-dom
5695
+ // Example Text: content did not match. Server: "A" Client: "B"
5696
+ exceptionValue.match(/(hydration|content does not match|did not match)/i)
5697
+ ) {
5698
+ const breadcrumb = createBreadcrumb({
5699
+ category: 'replay.hydrate-error',
5700
+ });
5701
+ addBreadcrumbEvent(replay, breadcrumb);
5702
+ }
5703
+ }
5704
+
5642
5705
  /**
5643
5706
  * Returns true if we think the given event is an error originating inside of rrweb.
5644
5707
  */
@@ -6963,6 +7026,7 @@ function addGlobalListeners(replay) {
6963
7026
 
6964
7027
  // If a custom client has no hooks yet, we continue to use the "old" implementation
6965
7028
  if (hasHooks(client)) {
7029
+ client.on('beforeSendEvent', handleBeforeSendEvent(replay));
6966
7030
  client.on('afterSendEvent', handleAfterSendEvent(replay));
6967
7031
  client.on('createDsc', (dsc) => {
6968
7032
  const replayId = replay.getSessionId();
@@ -7870,6 +7934,7 @@ class ReplayContainer {
7870
7934
  */
7871
7935
  startRecording() {
7872
7936
  try {
7937
+ const canvas = this._options._experiments.canvas;
7873
7938
  this._stopRecording = record({
7874
7939
  ...this._recordingOptions,
7875
7940
  // When running in error sampling mode, we need to overwrite `checkoutEveryNms`
@@ -7878,6 +7943,12 @@ class ReplayContainer {
7878
7943
  ...(this.recordingMode === 'buffer' && { checkoutEveryNms: BUFFER_CHECKOUT_TIME }),
7879
7944
  emit: getHandleRecordingEmit(this),
7880
7945
  onMutation: this._onMutationHandler,
7946
+ ...(canvas && {
7947
+ recordCanvas: true,
7948
+ sampling: { canvas: canvas.fps || 4 },
7949
+ dataURLOptions: { quality: canvas.quality || 0.6 },
7950
+ getCanvasManager: canvas.manager,
7951
+ }),
7881
7952
  });
7882
7953
  } catch (err) {
7883
7954
  this._handleException(err);