@sentry/replay 7.53.1 → 7.54.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
- import { getCurrentHub, addBreadcrumb, addGlobalEventProcessor, prepareEvent, setContext, captureException } from '@sentry/core';
2
- import { GLOBAL_OBJ, logger, normalize, htmlTreeAsString, uuid4, SENTRY_XHR_DATA_KEY, dropUndefinedKeys, stringMatchesSomePattern, addInstrumentationHandler, browserPerformanceTimeOrigin, createEnvelope, createEventEnvelopeHeaders, getSdkMetadataForEnvelopeHeader, isNodeEnv } from '@sentry/utils';
1
+ import { getCurrentHub, addGlobalEventProcessor, prepareEvent, setContext, captureException } from '@sentry/core';
2
+ import { GLOBAL_OBJ, normalize, htmlTreeAsString, logger, uuid4, SENTRY_XHR_DATA_KEY, dropUndefinedKeys, stringMatchesSomePattern, addInstrumentationHandler, browserPerformanceTimeOrigin, createEnvelope, createEventEnvelopeHeaders, getSdkMetadataForEnvelopeHeader, isNodeEnv } from '@sentry/utils';
3
3
 
4
4
  // exporting a separate copy of `WINDOW` rather than exporting the one from `@sentry/browser`
5
5
  // prevents the browser package from being bundled in the CDN bundle, and avoids a
@@ -1133,7 +1133,7 @@ if (typeof window !== 'undefined' && window.Proxy && window.Reflect) {
1133
1133
  },
1134
1134
  });
1135
1135
  }
1136
- function throttle(func, wait, options = {}) {
1136
+ function throttle$1(func, wait, options = {}) {
1137
1137
  let timeout = null;
1138
1138
  let previous = 0;
1139
1139
  return function (arg) {
@@ -1868,7 +1868,7 @@ function initMoveObserver({ mousemoveCb, sampling, doc, mirror, }) {
1868
1868
  : 500;
1869
1869
  let positions = [];
1870
1870
  let timeBaseline;
1871
- const wrappedCb = throttle((source) => {
1871
+ const wrappedCb = throttle$1((source) => {
1872
1872
  const totalOffset = Date.now() - timeBaseline;
1873
1873
  callbackWrapper(mousemoveCb)(positions.map((p) => {
1874
1874
  p.timeOffset -= totalOffset;
@@ -1877,7 +1877,7 @@ function initMoveObserver({ mousemoveCb, sampling, doc, mirror, }) {
1877
1877
  positions = [];
1878
1878
  timeBaseline = null;
1879
1879
  }, callbackThreshold);
1880
- const updatePosition = throttle((evt) => {
1880
+ const updatePosition = throttle$1((evt) => {
1881
1881
  const target = getEventTarget(evt);
1882
1882
  const { clientX, clientY } = isTouchEvent(evt)
1883
1883
  ? evt.changedTouches[0]
@@ -1951,7 +1951,7 @@ function initMouseInteractionObserver({ mouseInteractionCb, doc, mirror, blockCl
1951
1951
  });
1952
1952
  }
1953
1953
  function initScrollObserver({ scrollCb, doc, mirror, blockClass, blockSelector, unblockSelector, sampling, }) {
1954
- const updatePosition = throttle((evt) => {
1954
+ const updatePosition = throttle$1((evt) => {
1955
1955
  const target = getEventTarget(evt);
1956
1956
  if (!target ||
1957
1957
  isBlocked(target, blockClass, blockSelector, unblockSelector)) {
@@ -1979,7 +1979,7 @@ function initScrollObserver({ scrollCb, doc, mirror, blockClass, blockSelector,
1979
1979
  function initViewportResizeObserver({ viewportResizeCb, }) {
1980
1980
  let lastH = -1;
1981
1981
  let lastW = -1;
1982
- const updateDimension = throttle(() => {
1982
+ const updateDimension = throttle$1(() => {
1983
1983
  const height = getWindowHeight();
1984
1984
  const width = getWindowWidth();
1985
1985
  if (lastH !== height || lastW !== width) {
@@ -2268,7 +2268,7 @@ function initStyleDeclarationObserver({ styleDeclarationCb, mirror }, { win }) {
2268
2268
  });
2269
2269
  }
2270
2270
  function initMediaInteractionObserver({ mediaInteractionCb, blockClass, blockSelector, unblockSelector, mirror, sampling, }) {
2271
- const handler = (type) => throttle(callbackWrapper((event) => {
2271
+ const handler = (type) => throttle$1(callbackWrapper((event) => {
2272
2272
  const target = getEventTarget(event);
2273
2273
  if (!target ||
2274
2274
  isBlocked(target, blockClass, blockSelector, unblockSelector)) {
@@ -3285,89 +3285,6 @@ var NodeType;
3285
3285
  NodeType[NodeType["Comment"] = 5] = "Comment";
3286
3286
  })(NodeType || (NodeType = {}));
3287
3287
 
3288
- /* eslint-disable @typescript-eslint/naming-convention */
3289
-
3290
- var EventType; (function (EventType) {
3291
- const DomContentLoaded = 0; EventType[EventType["DomContentLoaded"] = DomContentLoaded] = "DomContentLoaded";
3292
- const Load = 1; EventType[EventType["Load"] = Load] = "Load";
3293
- const FullSnapshot = 2; EventType[EventType["FullSnapshot"] = FullSnapshot] = "FullSnapshot";
3294
- const IncrementalSnapshot = 3; EventType[EventType["IncrementalSnapshot"] = IncrementalSnapshot] = "IncrementalSnapshot";
3295
- const Meta = 4; EventType[EventType["Meta"] = Meta] = "Meta";
3296
- const Custom = 5; EventType[EventType["Custom"] = Custom] = "Custom";
3297
- const Plugin = 6; EventType[EventType["Plugin"] = Plugin] = "Plugin";
3298
- })(EventType || (EventType = {}));
3299
-
3300
- /**
3301
- * This is a partial copy of rrweb's eventWithTime type which only contains the properties
3302
- * we specifcally need in the SDK.
3303
- */
3304
-
3305
- /**
3306
- * Converts a timestamp to ms, if it was in s, or keeps it as ms.
3307
- */
3308
- function timestampToMs(timestamp) {
3309
- const isMs = timestamp > 9999999999;
3310
- return isMs ? timestamp : timestamp * 1000;
3311
- }
3312
-
3313
- /**
3314
- * Add an event to the event buffer.
3315
- * `isCheckout` is true if this is either the very first event, or an event triggered by `checkoutEveryNms`.
3316
- */
3317
- async function addEvent(
3318
- replay,
3319
- event,
3320
- isCheckout,
3321
- ) {
3322
- if (!replay.eventBuffer) {
3323
- // This implies that `_isEnabled` is false
3324
- return null;
3325
- }
3326
-
3327
- if (replay.isPaused()) {
3328
- // Do not add to event buffer when recording is paused
3329
- return null;
3330
- }
3331
-
3332
- const timestampInMs = timestampToMs(event.timestamp);
3333
-
3334
- // Throw out events that happen more than 5 minutes ago. This can happen if
3335
- // page has been left open and idle for a long period of time and user
3336
- // comes back to trigger a new session. The performance entries rely on
3337
- // `performance.timeOrigin`, which is when the page first opened.
3338
- if (timestampInMs + replay.timeouts.sessionIdlePause < Date.now()) {
3339
- return null;
3340
- }
3341
-
3342
- try {
3343
- if (isCheckout) {
3344
- replay.eventBuffer.clear();
3345
- }
3346
-
3347
- const replayOptions = replay.getOptions();
3348
-
3349
- const eventAfterPossibleCallback =
3350
- typeof replayOptions.beforeAddRecordingEvent === 'function' && event.type === EventType.Custom
3351
- ? replayOptions.beforeAddRecordingEvent(event)
3352
- : event;
3353
-
3354
- if (!eventAfterPossibleCallback) {
3355
- return;
3356
- }
3357
-
3358
- return await replay.eventBuffer.addEvent(eventAfterPossibleCallback);
3359
- } catch (error) {
3360
- (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.error(error);
3361
- await replay.stop('addEvent');
3362
-
3363
- const client = getCurrentHub().getClient();
3364
-
3365
- if (client) {
3366
- client.recordDroppedEvent('internal_sdk_error', 'replay');
3367
- }
3368
- }
3369
- }
3370
-
3371
3288
  /**
3372
3289
  * Add a breadcrumb event to replay.
3373
3290
  */
@@ -3383,7 +3300,7 @@ function addBreadcrumbEvent(replay, breadcrumb) {
3383
3300
  }
3384
3301
 
3385
3302
  replay.addUpdate(() => {
3386
- void addEvent(replay, {
3303
+ void replay.throttledAddEvent({
3387
3304
  type: EventType$1.Custom,
3388
3305
  // TODO: We were converting from ms to seconds for breadcrumbs, spans,
3389
3306
  // but maybe we should just keep them as milliseconds
@@ -3724,7 +3641,7 @@ function getKeyboardBreadcrumb(event) {
3724
3641
  const { metaKey, shiftKey, ctrlKey, altKey, key, target } = event;
3725
3642
 
3726
3643
  // never capture for input fields
3727
- if (!target || isInputElement(target )) {
3644
+ if (!target || isInputElement(target ) || !key) {
3728
3645
  return null;
3729
3646
  }
3730
3647
 
@@ -3896,6 +3813,14 @@ function t(t){let e=t.length;for(;--e>=0;)t[e]=0}const e=new Uint8Array([0,0,0,0
3896
3813
 
3897
3814
  function e(){const e=new Blob([r]);return URL.createObjectURL(e)}
3898
3815
 
3816
+ /**
3817
+ * Converts a timestamp to ms, if it was in s, or keeps it as ms.
3818
+ */
3819
+ function timestampToMs(timestamp) {
3820
+ const isMs = timestamp > 9999999999;
3821
+ return isMs ? timestamp : timestamp * 1000;
3822
+ }
3823
+
3899
3824
  /**
3900
3825
  * A basic event buffer that does not do any compression.
3901
3826
  * Used as fallback if the compression worker cannot be loaded or is disabled.
@@ -4473,6 +4398,81 @@ function getSession({
4473
4398
  return { type: 'new', session: newSession };
4474
4399
  }
4475
4400
 
4401
+ /* eslint-disable @typescript-eslint/naming-convention */
4402
+
4403
+ var EventType; (function (EventType) {
4404
+ const DomContentLoaded = 0; EventType[EventType["DomContentLoaded"] = DomContentLoaded] = "DomContentLoaded";
4405
+ const Load = 1; EventType[EventType["Load"] = Load] = "Load";
4406
+ const FullSnapshot = 2; EventType[EventType["FullSnapshot"] = FullSnapshot] = "FullSnapshot";
4407
+ const IncrementalSnapshot = 3; EventType[EventType["IncrementalSnapshot"] = IncrementalSnapshot] = "IncrementalSnapshot";
4408
+ const Meta = 4; EventType[EventType["Meta"] = Meta] = "Meta";
4409
+ const Custom = 5; EventType[EventType["Custom"] = Custom] = "Custom";
4410
+ const Plugin = 6; EventType[EventType["Plugin"] = Plugin] = "Plugin";
4411
+ })(EventType || (EventType = {}));
4412
+
4413
+ /**
4414
+ * This is a partial copy of rrweb's eventWithTime type which only contains the properties
4415
+ * we specifcally need in the SDK.
4416
+ */
4417
+
4418
+ /**
4419
+ * Add an event to the event buffer.
4420
+ * `isCheckout` is true if this is either the very first event, or an event triggered by `checkoutEveryNms`.
4421
+ */
4422
+ async function addEvent(
4423
+ replay,
4424
+ event,
4425
+ isCheckout,
4426
+ ) {
4427
+ if (!replay.eventBuffer) {
4428
+ // This implies that `_isEnabled` is false
4429
+ return null;
4430
+ }
4431
+
4432
+ if (replay.isPaused()) {
4433
+ // Do not add to event buffer when recording is paused
4434
+ return null;
4435
+ }
4436
+
4437
+ const timestampInMs = timestampToMs(event.timestamp);
4438
+
4439
+ // Throw out events that happen more than 5 minutes ago. This can happen if
4440
+ // page has been left open and idle for a long period of time and user
4441
+ // comes back to trigger a new session. The performance entries rely on
4442
+ // `performance.timeOrigin`, which is when the page first opened.
4443
+ if (timestampInMs + replay.timeouts.sessionIdlePause < Date.now()) {
4444
+ return null;
4445
+ }
4446
+
4447
+ try {
4448
+ if (isCheckout) {
4449
+ replay.eventBuffer.clear();
4450
+ }
4451
+
4452
+ const replayOptions = replay.getOptions();
4453
+
4454
+ const eventAfterPossibleCallback =
4455
+ typeof replayOptions.beforeAddRecordingEvent === 'function' && event.type === EventType.Custom
4456
+ ? replayOptions.beforeAddRecordingEvent(event)
4457
+ : event;
4458
+
4459
+ if (!eventAfterPossibleCallback) {
4460
+ return;
4461
+ }
4462
+
4463
+ return await replay.eventBuffer.addEvent(eventAfterPossibleCallback);
4464
+ } catch (error) {
4465
+ (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.error(error);
4466
+ await replay.stop('addEvent');
4467
+
4468
+ const client = getCurrentHub().getClient();
4469
+
4470
+ if (client) {
4471
+ client.recordDroppedEvent('internal_sdk_error', 'replay');
4472
+ }
4473
+ }
4474
+ }
4475
+
4476
4476
  /** If the event is an error event */
4477
4477
  function isErrorEvent(event) {
4478
4478
  return !event.type;
@@ -4647,13 +4647,6 @@ function handleGlobalEventListener(
4647
4647
  event.tags = { ...event.tags, replayId: replay.getSessionId() };
4648
4648
  }
4649
4649
 
4650
- if ((typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && replay.getOptions()._experiments.traceInternals && isErrorEvent(event)) {
4651
- const exc = getEventExceptionValues(event);
4652
- addInternalBreadcrumb({
4653
- message: `Tagging event (${event.event_id}) - ${event.message} - ${exc.type}: ${exc.value}`,
4654
- });
4655
- }
4656
-
4657
4650
  // In cases where a custom client is used that does not support the new hooks (yet),
4658
4651
  // we manually call this hook method here
4659
4652
  if (afterSendHandler) {
@@ -4665,34 +4658,15 @@ function handleGlobalEventListener(
4665
4658
  };
4666
4659
  }
4667
4660
 
4668
- function addInternalBreadcrumb(arg) {
4669
- const { category, level, message, ...rest } = arg;
4670
-
4671
- addBreadcrumb({
4672
- category: category || 'console',
4673
- level: level || 'debug',
4674
- message: `[debug]: ${message}`,
4675
- ...rest,
4676
- });
4677
- }
4678
-
4679
- function getEventExceptionValues(event) {
4680
- return {
4681
- type: 'Unknown',
4682
- value: 'n/a',
4683
- ...(event.exception && event.exception.values && event.exception.values[0]),
4684
- };
4685
- }
4686
-
4687
4661
  /**
4688
- * Create a "span" for each performance entry. The parent transaction is `this.replayEvent`.
4662
+ * Create a "span" for each performance entry.
4689
4663
  */
4690
4664
  function createPerformanceSpans(
4691
4665
  replay,
4692
4666
  entries,
4693
4667
  ) {
4694
- return entries.map(({ type, start, end, name, data }) =>
4695
- addEvent(replay, {
4668
+ return entries.map(({ type, start, end, name, data }) => {
4669
+ const response = replay.throttledAddEvent({
4696
4670
  type: EventType$1.Custom,
4697
4671
  timestamp: start,
4698
4672
  data: {
@@ -4705,8 +4679,11 @@ function createPerformanceSpans(
4705
4679
  data,
4706
4680
  },
4707
4681
  },
4708
- }),
4709
- );
4682
+ });
4683
+
4684
+ // If response is a string, it means its either THROTTLED or SKIPPED
4685
+ return typeof response === 'string' ? Promise.resolve(null) : response;
4686
+ });
4710
4687
  }
4711
4688
 
4712
4689
  function handleHistory(handlerData) {
@@ -6055,6 +6032,16 @@ function addGlobalListeners(replay) {
6055
6032
  dsc.replay_id = replayId;
6056
6033
  }
6057
6034
  });
6035
+
6036
+ client.on('startTransaction', transaction => {
6037
+ replay.lastTransaction = transaction;
6038
+ });
6039
+
6040
+ // We may be missing the initial startTransaction due to timing issues,
6041
+ // so we capture it on finish again.
6042
+ client.on('finishTransaction', transaction => {
6043
+ replay.lastTransaction = transaction;
6044
+ });
6058
6045
  }
6059
6046
  }
6060
6047
 
@@ -6778,6 +6765,62 @@ async function sendReplay(
6778
6765
  }
6779
6766
  }
6780
6767
 
6768
+ const THROTTLED = '__THROTTLED';
6769
+ const SKIPPED = '__SKIPPED';
6770
+
6771
+ /**
6772
+ * Create a throttled function off a given function.
6773
+ * When calling the throttled function, it will call the original function only
6774
+ * if it hasn't been called more than `maxCount` times in the last `durationSeconds`.
6775
+ *
6776
+ * Returns `THROTTLED` if throttled for the first time, after that `SKIPPED`,
6777
+ * or else the return value of the original function.
6778
+ */
6779
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6780
+ function throttle(
6781
+ fn,
6782
+ maxCount,
6783
+ durationSeconds,
6784
+ ) {
6785
+ const counter = new Map();
6786
+
6787
+ const _cleanup = (now) => {
6788
+ const threshold = now - durationSeconds;
6789
+ counter.forEach((_value, key) => {
6790
+ if (key < threshold) {
6791
+ counter.delete(key);
6792
+ }
6793
+ });
6794
+ };
6795
+
6796
+ const _getTotalCount = () => {
6797
+ return [...counter.values()].reduce((a, b) => a + b, 0);
6798
+ };
6799
+
6800
+ let isThrottled = false;
6801
+
6802
+ return (...rest) => {
6803
+ // Date in second-precision, which we use as basis for the throttling
6804
+ const now = Math.floor(Date.now() / 1000);
6805
+
6806
+ // First, make sure to delete any old entries
6807
+ _cleanup(now);
6808
+
6809
+ // If already over limit, do nothing
6810
+ if (_getTotalCount() >= maxCount) {
6811
+ const wasThrottled = isThrottled;
6812
+ isThrottled = true;
6813
+ return wasThrottled ? SKIPPED : THROTTLED;
6814
+ }
6815
+
6816
+ isThrottled = false;
6817
+ const count = counter.get(now) || 0;
6818
+ counter.set(now, count + 1);
6819
+
6820
+ return fn(...rest);
6821
+ };
6822
+ }
6823
+
6781
6824
  /* eslint-disable max-lines */ // TODO: We might want to split this file up
6782
6825
 
6783
6826
  /**
@@ -6800,6 +6843,11 @@ class ReplayContainer {
6800
6843
  */
6801
6844
  __init3() {this.recordingMode = 'session';}
6802
6845
 
6846
+ /**
6847
+ * The current or last active transcation.
6848
+ * This is only available when performance is enabled.
6849
+ */
6850
+
6803
6851
  /**
6804
6852
  * These are here so we can overwrite them in tests etc.
6805
6853
  * @hidden
@@ -6866,6 +6914,14 @@ class ReplayContainer {
6866
6914
  this._debouncedFlush = debounce(() => this._flush(), this._options.flushMinDelay, {
6867
6915
  maxWait: this._options.flushMaxDelay,
6868
6916
  });
6917
+
6918
+ this._throttledAddEvent = throttle(
6919
+ (event, isCheckout) => addEvent(this, event, isCheckout),
6920
+ // Max 300 events...
6921
+ 300,
6922
+ // ... per 5s
6923
+ 5,
6924
+ );
6869
6925
  }
6870
6926
 
6871
6927
  /** Get the event context. */
@@ -7295,6 +7351,52 @@ class ReplayContainer {
7295
7351
  this._context.urls.push(url);
7296
7352
  }
7297
7353
 
7354
+ /**
7355
+ * Add a breadcrumb event, that may be throttled.
7356
+ * If it was throttled, we add a custom breadcrumb to indicate that.
7357
+ */
7358
+ throttledAddEvent(
7359
+ event,
7360
+ isCheckout,
7361
+ ) {
7362
+ const res = this._throttledAddEvent(event, isCheckout);
7363
+
7364
+ // If this is THROTTLED, it means we have throttled the event for the first time
7365
+ // In this case, we want to add a breadcrumb indicating that something was skipped
7366
+ if (res === THROTTLED) {
7367
+ const breadcrumb = createBreadcrumb({
7368
+ category: 'replay.throttled',
7369
+ });
7370
+
7371
+ this.addUpdate(() => {
7372
+ void addEvent(this, {
7373
+ type: EventType$1.Custom,
7374
+ timestamp: breadcrumb.timestamp || 0,
7375
+ data: {
7376
+ tag: 'breadcrumb',
7377
+ payload: breadcrumb,
7378
+ metric: true,
7379
+ },
7380
+ });
7381
+ });
7382
+ }
7383
+
7384
+ return res;
7385
+ }
7386
+
7387
+ /**
7388
+ * This will get the parametrized route name of the current page.
7389
+ * This is only available if performance is enabled, and if an instrumented router is used.
7390
+ */
7391
+ getCurrentRoute() {
7392
+ const lastTransaction = this.lastTransaction || getCurrentHub().getScope().getTransaction();
7393
+ if (!lastTransaction || !['route', 'custom'].includes(lastTransaction.metadata.source)) {
7394
+ return undefined;
7395
+ }
7396
+
7397
+ return lastTransaction.name;
7398
+ }
7399
+
7298
7400
  /**
7299
7401
  * Initialize and start all listeners to varying events (DOM,
7300
7402
  * Performance Observer, Recording, Sentry SDK, etc)
@@ -7533,7 +7635,7 @@ class ReplayContainer {
7533
7635
  */
7534
7636
  _createCustomBreadcrumb(breadcrumb) {
7535
7637
  this.addUpdate(() => {
7536
- void addEvent(this, {
7638
+ void this.throttledAddEvent({
7537
7639
  type: EventType$1.Custom,
7538
7640
  timestamp: breadcrumb.timestamp || 0,
7539
7641
  data: {
@@ -7732,8 +7834,8 @@ class ReplayContainer {
7732
7834
  __init18() {this._onMutationHandler = (mutations) => {
7733
7835
  const count = mutations.length;
7734
7836
 
7735
- const mutationLimit = this._options._experiments.mutationLimit || 0;
7736
- const mutationBreadcrumbLimit = this._options._experiments.mutationBreadcrumbLimit || 1000;
7837
+ const mutationLimit = this._options.mutationLimit;
7838
+ const mutationBreadcrumbLimit = this._options.mutationBreadcrumbLimit;
7737
7839
  const overMutationLimit = mutationLimit && count > mutationLimit;
7738
7840
 
7739
7841
  // Create a breadcrumb if a lot of mutations happen at the same time
@@ -7743,15 +7845,15 @@ class ReplayContainer {
7743
7845
  category: 'replay.mutations',
7744
7846
  data: {
7745
7847
  count,
7848
+ limit: overMutationLimit,
7746
7849
  },
7747
7850
  });
7748
7851
  this._createCustomBreadcrumb(breadcrumb);
7749
7852
  }
7750
7853
 
7854
+ // Stop replay if over the mutation limit
7751
7855
  if (overMutationLimit) {
7752
- // We want to skip doing an incremental snapshot if there are too many mutations
7753
- // Instead, we do a full snapshot
7754
- this._triggerFullSnapshot(false);
7856
+ void this.stop('mutationLimit');
7755
7857
  return false;
7756
7858
  }
7757
7859
 
@@ -7905,6 +8007,9 @@ class Replay {
7905
8007
  maskAllInputs = true,
7906
8008
  blockAllMedia = true,
7907
8009
 
8010
+ mutationBreadcrumbLimit = 750,
8011
+ mutationLimit = 10000,
8012
+
7908
8013
  networkDetailAllowUrls = [],
7909
8014
  networkCaptureBodies = true,
7910
8015
  networkRequestHeaders = [],
@@ -7972,6 +8077,8 @@ class Replay {
7972
8077
  blockAllMedia,
7973
8078
  maskAllInputs,
7974
8079
  maskAllText,
8080
+ mutationBreadcrumbLimit,
8081
+ mutationLimit,
7975
8082
  networkDetailAllowUrls,
7976
8083
  networkCaptureBodies,
7977
8084
  networkRequestHeaders: _getMergedNetworkHeaders(networkRequestHeaders),