@sentry/replay 7.51.0 → 7.51.1

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
@@ -73,7 +73,7 @@ function maskInputValue({ input, maskInputSelector, unmaskInputSelector, maskInp
73
73
  if (unmaskInputSelector && input.matches(unmaskInputSelector)) {
74
74
  return text;
75
75
  }
76
- if (input.hasAttribute('rr_is_password')) {
76
+ if (input.hasAttribute('data-rr-is-password')) {
77
77
  type = 'password';
78
78
  }
79
79
  if (isInputTypeMasked({ maskInputOptions, tagName, type }) ||
@@ -106,6 +106,21 @@ function is2DCanvasBlank(canvas) {
106
106
  }
107
107
  return true;
108
108
  }
109
+ function getInputType(element) {
110
+ const type = element.type;
111
+ return element.hasAttribute('data-rr-is-password')
112
+ ? 'password'
113
+ : type
114
+ ? type.toLowerCase()
115
+ : null;
116
+ }
117
+ function getInputValue(el, tagName, type) {
118
+ typeof type === 'string' ? type.toLowerCase() : '';
119
+ if (tagName === 'INPUT' && (type === 'radio' || type === 'checkbox')) {
120
+ return el.getAttribute('value') || '';
121
+ }
122
+ return el.value;
123
+ }
109
124
 
110
125
  let _id = 1;
111
126
  const tagNameRegex = new RegExp('[^a-z0-9-_:]');
@@ -144,6 +159,13 @@ function getCssRuleString(rule) {
144
159
  catch (_a) {
145
160
  }
146
161
  }
162
+ return validateStringifiedCssRule(cssStringified);
163
+ }
164
+ function validateStringifiedCssRule(cssStringified) {
165
+ if (cssStringified.indexOf(':') > -1) {
166
+ const regex = /(\[(?:[\w-]+)[^\\])(:(?:[\w-]+)\])/gm;
167
+ return cssStringified.replace(regex, '$1\\$2');
168
+ }
147
169
  return cssStringified;
148
170
  }
149
171
  function isCSSImportRule(rule) {
@@ -152,7 +174,7 @@ function isCSSImportRule(rule) {
152
174
  function stringifyStyleSheet(sheet) {
153
175
  return sheet.cssRules
154
176
  ? Array.from(sheet.cssRules)
155
- .map((rule) => rule.cssText || '')
177
+ .map((rule) => rule.cssText ? validateStringifiedCssRule(rule.cssText) : '')
156
178
  .join('')
157
179
  : '';
158
180
  }
@@ -487,14 +509,15 @@ function serializeNode(n, options) {
487
509
  tagName === 'select' ||
488
510
  tagName === 'option') {
489
511
  const el = n;
490
- const value = getInputValue(tagName, el, attributes);
512
+ const type = getInputType(el);
513
+ const value = getInputValue(el, tagName.toUpperCase(), type);
491
514
  const checked = n.checked;
492
- if (attributes.type !== 'submit' &&
493
- attributes.type !== 'button' &&
515
+ if (type !== 'submit' &&
516
+ type !== 'button' &&
494
517
  value) {
495
518
  attributes.value = maskInputValue({
496
519
  input: el,
497
- type: attributes.type,
520
+ type,
498
521
  tagName,
499
522
  value,
500
523
  maskInputSelector,
@@ -967,15 +990,8 @@ function snapshot(n, options) {
967
990
  function skipAttribute(tagName, attributeName, value) {
968
991
  return ((tagName === 'video' || tagName === 'audio') && attributeName === 'autoplay');
969
992
  }
970
- function getInputValue(tagName, el, attributes) {
971
- if (tagName === 'input' &&
972
- (attributes.type === 'radio' || attributes.type === 'checkbox')) {
973
- return el.getAttribute('value') || '';
974
- }
975
- return el.value;
976
- }
977
993
 
978
- var EventType;
994
+ var EventType$1;
979
995
  (function (EventType) {
980
996
  EventType[EventType["DomContentLoaded"] = 0] = "DomContentLoaded";
981
997
  EventType[EventType["Load"] = 1] = "Load";
@@ -984,7 +1000,7 @@ var EventType;
984
1000
  EventType[EventType["Meta"] = 4] = "Meta";
985
1001
  EventType[EventType["Custom"] = 5] = "Custom";
986
1002
  EventType[EventType["Plugin"] = 6] = "Plugin";
987
- })(EventType || (EventType = {}));
1003
+ })(EventType$1 || (EventType$1 = {}));
988
1004
  var IncrementalSource;
989
1005
  (function (IncrementalSource) {
990
1006
  IncrementalSource[IncrementalSource["Mutation"] = 0] = "Mutation";
@@ -1599,9 +1615,9 @@ class MutationBuffer {
1599
1615
  this.attributes.push(item);
1600
1616
  }
1601
1617
  if (m.attributeName === 'type' &&
1602
- m.target.tagName === 'INPUT' &&
1618
+ target.tagName === 'INPUT' &&
1603
1619
  (m.oldValue || '').toLowerCase() === 'password') {
1604
- m.target.setAttribute('rr_is_password', 'true');
1620
+ target.setAttribute('data-rr-is-password', 'true');
1605
1621
  }
1606
1622
  if (m.attributeName === 'style') {
1607
1623
  const old = this.doc.createElement('span');
@@ -1998,27 +2014,25 @@ function initInputObserver({ inputCb, doc, mirror, blockClass, blockSelector, un
1998
2014
  isBlocked(target, blockClass, blockSelector, unblockSelector)) {
1999
2015
  return;
2000
2016
  }
2001
- let type = target.type;
2002
- if (target.classList.contains(ignoreClass) ||
2003
- (ignoreSelector && target.matches(ignoreSelector))) {
2017
+ const el = target;
2018
+ const type = getInputType(el);
2019
+ if (el.classList.contains(ignoreClass) ||
2020
+ (ignoreSelector && el.matches(ignoreSelector))) {
2004
2021
  return;
2005
2022
  }
2006
- let text = target.value;
2023
+ let text = getInputValue(el, tagName, type);
2007
2024
  let isChecked = false;
2008
- if (target.hasAttribute('rr_is_password')) {
2009
- type = 'password';
2010
- }
2011
2025
  if (type === 'radio' || type === 'checkbox') {
2012
2026
  isChecked = target.checked;
2013
2027
  }
2014
- else if (hasInputMaskOptions({
2028
+ if (hasInputMaskOptions({
2015
2029
  maskInputOptions,
2016
2030
  maskInputSelector,
2017
2031
  tagName,
2018
2032
  type,
2019
2033
  })) {
2020
2034
  text = maskInputValue({
2021
- input: target,
2035
+ input: el,
2022
2036
  maskInputOptions,
2023
2037
  maskInputSelector,
2024
2038
  unmaskInputSelector,
@@ -2035,8 +2049,18 @@ function initInputObserver({ inputCb, doc, mirror, blockClass, blockSelector, un
2035
2049
  .querySelectorAll(`input[type="radio"][name="${name}"]`)
2036
2050
  .forEach((el) => {
2037
2051
  if (el !== target) {
2052
+ const text = maskInputValue({
2053
+ input: el,
2054
+ maskInputOptions,
2055
+ maskInputSelector,
2056
+ unmaskInputSelector,
2057
+ tagName,
2058
+ type,
2059
+ value: getInputValue(el, tagName, type),
2060
+ maskInputFn,
2061
+ });
2038
2062
  cbWithDedup(el, callbackWrapper(wrapEventWithUserTriggeredFlag)({
2039
- text: el.value,
2063
+ text,
2040
2064
  isChecked: !isChecked,
2041
2065
  userTriggered: false,
2042
2066
  }, userTriggeredOnInput));
@@ -2949,17 +2973,17 @@ function record(options = {}) {
2949
2973
  wrappedEmit = (e, isCheckout) => {
2950
2974
  var _a;
2951
2975
  if (((_a = mutationBuffers[0]) === null || _a === void 0 ? void 0 : _a.isFrozen()) &&
2952
- e.type !== EventType.FullSnapshot &&
2953
- !(e.type === EventType.IncrementalSnapshot &&
2976
+ e.type !== EventType$1.FullSnapshot &&
2977
+ !(e.type === EventType$1.IncrementalSnapshot &&
2954
2978
  e.data.source === IncrementalSource.Mutation)) {
2955
2979
  mutationBuffers.forEach((buf) => buf.unfreeze());
2956
2980
  }
2957
2981
  emit(eventProcessor(e), isCheckout);
2958
- if (e.type === EventType.FullSnapshot) {
2982
+ if (e.type === EventType$1.FullSnapshot) {
2959
2983
  lastFullSnapshotEvent = e;
2960
2984
  incrementalSnapshotCount = 0;
2961
2985
  }
2962
- else if (e.type === EventType.IncrementalSnapshot) {
2986
+ else if (e.type === EventType$1.IncrementalSnapshot) {
2963
2987
  if (e.data.source === IncrementalSource.Mutation &&
2964
2988
  e.data.isAttachIframe) {
2965
2989
  return;
@@ -2975,16 +2999,16 @@ function record(options = {}) {
2975
2999
  };
2976
3000
  const wrappedMutationEmit = (m) => {
2977
3001
  wrappedEmit(wrapEvent({
2978
- type: EventType.IncrementalSnapshot,
3002
+ type: EventType$1.IncrementalSnapshot,
2979
3003
  data: Object.assign({ source: IncrementalSource.Mutation }, m),
2980
3004
  }));
2981
3005
  };
2982
3006
  const wrappedScrollEmit = (p) => wrappedEmit(wrapEvent({
2983
- type: EventType.IncrementalSnapshot,
3007
+ type: EventType$1.IncrementalSnapshot,
2984
3008
  data: Object.assign({ source: IncrementalSource.Scroll }, p),
2985
3009
  }));
2986
3010
  const wrappedCanvasMutationEmit = (p) => wrappedEmit(wrapEvent({
2987
- type: EventType.IncrementalSnapshot,
3011
+ type: EventType$1.IncrementalSnapshot,
2988
3012
  data: Object.assign({ source: IncrementalSource.CanvasMutation }, p),
2989
3013
  }));
2990
3014
  const iframeManager = new IframeManager({
@@ -3029,7 +3053,7 @@ function record(options = {}) {
3029
3053
  takeFullSnapshot = (isCheckout = false) => {
3030
3054
  var _a, _b, _c, _d;
3031
3055
  wrappedEmit(wrapEvent({
3032
- type: EventType.Meta,
3056
+ type: EventType$1.Meta,
3033
3057
  data: {
3034
3058
  href: window.location.href,
3035
3059
  width: getWindowWidth(),
@@ -3072,7 +3096,7 @@ function record(options = {}) {
3072
3096
  }
3073
3097
  mirror.map = idNodeMap;
3074
3098
  wrappedEmit(wrapEvent({
3075
- type: EventType.FullSnapshot,
3099
+ type: EventType$1.FullSnapshot,
3076
3100
  data: {
3077
3101
  node,
3078
3102
  initialOffset: {
@@ -3097,7 +3121,7 @@ function record(options = {}) {
3097
3121
  const handlers = [];
3098
3122
  handlers.push(on('DOMContentLoaded', () => {
3099
3123
  wrappedEmit(wrapEvent({
3100
- type: EventType.DomContentLoaded,
3124
+ type: EventType$1.DomContentLoaded,
3101
3125
  data: {},
3102
3126
  }));
3103
3127
  }));
@@ -3107,40 +3131,40 @@ function record(options = {}) {
3107
3131
  onMutation,
3108
3132
  mutationCb: wrappedMutationEmit,
3109
3133
  mousemoveCb: (positions, source) => wrappedEmit(wrapEvent({
3110
- type: EventType.IncrementalSnapshot,
3134
+ type: EventType$1.IncrementalSnapshot,
3111
3135
  data: {
3112
3136
  source,
3113
3137
  positions,
3114
3138
  },
3115
3139
  })),
3116
3140
  mouseInteractionCb: (d) => wrappedEmit(wrapEvent({
3117
- type: EventType.IncrementalSnapshot,
3141
+ type: EventType$1.IncrementalSnapshot,
3118
3142
  data: Object.assign({ source: IncrementalSource.MouseInteraction }, d),
3119
3143
  })),
3120
3144
  scrollCb: wrappedScrollEmit,
3121
3145
  viewportResizeCb: (d) => wrappedEmit(wrapEvent({
3122
- type: EventType.IncrementalSnapshot,
3146
+ type: EventType$1.IncrementalSnapshot,
3123
3147
  data: Object.assign({ source: IncrementalSource.ViewportResize }, d),
3124
3148
  })),
3125
3149
  inputCb: (v) => wrappedEmit(wrapEvent({
3126
- type: EventType.IncrementalSnapshot,
3150
+ type: EventType$1.IncrementalSnapshot,
3127
3151
  data: Object.assign({ source: IncrementalSource.Input }, v),
3128
3152
  })),
3129
3153
  mediaInteractionCb: (p) => wrappedEmit(wrapEvent({
3130
- type: EventType.IncrementalSnapshot,
3154
+ type: EventType$1.IncrementalSnapshot,
3131
3155
  data: Object.assign({ source: IncrementalSource.MediaInteraction }, p),
3132
3156
  })),
3133
3157
  styleSheetRuleCb: (r) => wrappedEmit(wrapEvent({
3134
- type: EventType.IncrementalSnapshot,
3158
+ type: EventType$1.IncrementalSnapshot,
3135
3159
  data: Object.assign({ source: IncrementalSource.StyleSheetRule }, r),
3136
3160
  })),
3137
3161
  styleDeclarationCb: (r) => wrappedEmit(wrapEvent({
3138
- type: EventType.IncrementalSnapshot,
3162
+ type: EventType$1.IncrementalSnapshot,
3139
3163
  data: Object.assign({ source: IncrementalSource.StyleDeclaration }, r),
3140
3164
  })),
3141
3165
  canvasMutationCb: wrappedCanvasMutationEmit,
3142
3166
  fontCb: (p) => wrappedEmit(wrapEvent({
3143
- type: EventType.IncrementalSnapshot,
3167
+ type: EventType$1.IncrementalSnapshot,
3144
3168
  data: Object.assign({ source: IncrementalSource.Font }, p),
3145
3169
  })),
3146
3170
  blockClass,
@@ -3173,7 +3197,7 @@ function record(options = {}) {
3173
3197
  observer: p.observer,
3174
3198
  options: p.options,
3175
3199
  callback: (payload) => wrappedEmit(wrapEvent({
3176
- type: EventType.Plugin,
3200
+ type: EventType$1.Plugin,
3177
3201
  data: {
3178
3202
  plugin: p.name,
3179
3203
  payload,
@@ -3201,7 +3225,7 @@ function record(options = {}) {
3201
3225
  else {
3202
3226
  handlers.push(on('load', () => {
3203
3227
  wrappedEmit(wrapEvent({
3204
- type: EventType.Load,
3228
+ type: EventType$1.Load,
3205
3229
  data: {},
3206
3230
  }));
3207
3231
  init();
@@ -3220,7 +3244,7 @@ record.addCustomEvent = (tag, payload) => {
3220
3244
  throw new Error('please add custom event after start recording');
3221
3245
  }
3222
3246
  wrappedEmit(wrapEvent({
3223
- type: EventType.Custom,
3247
+ type: EventType$1.Custom,
3224
3248
  data: {
3225
3249
  tag,
3226
3250
  payload,
@@ -3399,6 +3423,11 @@ class EventBufferArray {
3399
3423
  return this.events.length > 0;
3400
3424
  }
3401
3425
 
3426
+ /** @inheritdoc */
3427
+ get type() {
3428
+ return 'sync';
3429
+ }
3430
+
3402
3431
  /** @inheritdoc */
3403
3432
  destroy() {
3404
3433
  this.events = [];
@@ -3554,6 +3583,11 @@ class EventBufferCompressionWorker {
3554
3583
  return !!this._earliestTimestamp;
3555
3584
  }
3556
3585
 
3586
+ /** @inheritdoc */
3587
+ get type() {
3588
+ return 'worker';
3589
+ }
3590
+
3557
3591
  /**
3558
3592
  * Ensure the worker is ready (or not).
3559
3593
  * This will either resolve when the worker is ready, or reject if an error occured.
@@ -3636,6 +3670,11 @@ class EventBufferProxy {
3636
3670
  this._ensureWorkerIsLoadedPromise = this._ensureWorkerIsLoaded();
3637
3671
  }
3638
3672
 
3673
+ /** @inheritdoc */
3674
+ get type() {
3675
+ return this._used.type;
3676
+ }
3677
+
3639
3678
  /** @inheritDoc */
3640
3679
  get hasEvents() {
3641
3680
  return this._used.hasEvents;
@@ -4041,22 +4080,18 @@ function handleAfterSendEvent(replay) {
4041
4080
  return;
4042
4081
  }
4043
4082
 
4044
- // Add error to list of errorIds of replay
4083
+ // Add error to list of errorIds of replay. This is ok to do even if not
4084
+ // sampled because context will get reset at next checkout.
4085
+ // XXX: There is also a race condition where it's possible to capture an
4086
+ // error to Sentry before Replay SDK has loaded, but response returns after
4087
+ // it was loaded, and this gets called.
4045
4088
  if (event.event_id) {
4046
4089
  replay.getContext().errorIds.add(event.event_id);
4047
4090
  }
4048
4091
 
4049
- // Trigger error recording
4092
+ // If error event is tagged with replay id it means it was sampled (when in buffer mode)
4050
4093
  // Need to be very careful that this does not cause an infinite loop
4051
- if (
4052
- replay.recordingMode === 'buffer' &&
4053
- event.exception &&
4054
- event.message !== UNABLE_TO_SEND_REPLAY // ignore this error because otherwise we could loop indefinitely with trying to capture replay and failing
4055
- ) {
4056
- if (!isSampled(replay.getOptions().errorSampleRate)) {
4057
- return;
4058
- }
4059
-
4094
+ if (replay.recordingMode === 'buffer' && event.tags && event.tags.replayId) {
4060
4095
  setTimeout(() => {
4061
4096
  // Capture current event buffer as new replay
4062
4097
  void replay.sendBufferedReplayOrFlush();
@@ -4120,7 +4155,7 @@ function addBreadcrumbEvent(replay, breadcrumb) {
4120
4155
 
4121
4156
  replay.addUpdate(() => {
4122
4157
  void addEvent(replay, {
4123
- type: EventType.Custom,
4158
+ type: EventType$1.Custom,
4124
4159
  // TODO: We were converting from ms to seconds for breadcrumbs, spans,
4125
4160
  // but maybe we should just keep them as milliseconds
4126
4161
  timestamp: (breadcrumb.timestamp || 0) * 1000,
@@ -4188,15 +4223,18 @@ const handleDomListener =
4188
4223
 
4189
4224
  /**
4190
4225
  * An event handler to react to DOM events.
4226
+ * Exported for tests only.
4191
4227
  */
4192
4228
  function handleDom(handlerData) {
4193
4229
  let target;
4194
4230
  let targetNode;
4195
4231
 
4232
+ const isClick = handlerData.name === 'click';
4233
+
4196
4234
  // Accessing event.target can throw (see getsentry/raven-js#838, #768)
4197
4235
  try {
4198
- targetNode = getTargetNode(handlerData);
4199
- target = htmlTreeAsString(targetNode);
4236
+ targetNode = isClick ? getClickTargetNode(handlerData.event) : getTargetNode(handlerData.event);
4237
+ target = htmlTreeAsString(targetNode, { maxStringLength: 200 });
4200
4238
  } catch (e) {
4201
4239
  target = '<unknown>';
4202
4240
  }
@@ -4230,12 +4268,29 @@ function handleDom(handlerData) {
4230
4268
  });
4231
4269
  }
4232
4270
 
4233
- function getTargetNode(handlerData) {
4234
- if (isEventWithTarget(handlerData.event)) {
4235
- return handlerData.event.target;
4271
+ function getTargetNode(event) {
4272
+ if (isEventWithTarget(event)) {
4273
+ return event.target;
4236
4274
  }
4237
4275
 
4238
- return handlerData.event;
4276
+ return event;
4277
+ }
4278
+
4279
+ const INTERACTIVE_SELECTOR = 'button,a';
4280
+
4281
+ // For clicks, we check if the target is inside of a button or link
4282
+ // If so, we use this as the target instead
4283
+ // This is useful because if you click on the image in <button><img></button>,
4284
+ // The target will be the image, not the button, which we don't want here
4285
+ function getClickTargetNode(event) {
4286
+ const target = getTargetNode(event);
4287
+
4288
+ if (!target || !(target instanceof Element)) {
4289
+ return target;
4290
+ }
4291
+
4292
+ const closestInteractive = target.closest(INTERACTIVE_SELECTOR);
4293
+ return closestInteractive || target;
4239
4294
  }
4240
4295
 
4241
4296
  function isEventWithTarget(event) {
@@ -4265,6 +4320,30 @@ function isRrwebError(event, hint) {
4265
4320
  });
4266
4321
  }
4267
4322
 
4323
+ /**
4324
+ * Determine if event should be sampled (only applies in buffer mode).
4325
+ * When an event is captured by `hanldleGlobalEvent`, when in buffer mode
4326
+ * we determine if we want to sample the error or not.
4327
+ */
4328
+ function shouldSampleForBufferEvent(replay, event) {
4329
+ if (replay.recordingMode !== 'buffer') {
4330
+ return false;
4331
+ }
4332
+
4333
+ // ignore this error because otherwise we could loop indefinitely with
4334
+ // trying to capture replay and failing
4335
+ if (event.message === UNABLE_TO_SEND_REPLAY) {
4336
+ return false;
4337
+ }
4338
+
4339
+ // Require the event to be an error event & to have an exception
4340
+ if (!event.exception || event.type) {
4341
+ return false;
4342
+ }
4343
+
4344
+ return isSampled(replay.getOptions().errorSampleRate);
4345
+ }
4346
+
4268
4347
  /**
4269
4348
  * Returns a listener to be added to `addGlobalEventProcessor(listener)`.
4270
4349
  */
@@ -4294,8 +4373,16 @@ function handleGlobalEventListener(
4294
4373
  return null;
4295
4374
  }
4296
4375
 
4297
- // Only tag transactions with replayId if not waiting for an error
4298
- if (isErrorEvent(event) || (isTransactionEvent(event) && replay.recordingMode === 'session')) {
4376
+ // When in buffer mode, we decide to sample here.
4377
+ // Later, in `handleAfterSendEvent`, if the replayId is set, we know that we sampled
4378
+ // And convert the buffer session to a full session
4379
+ const isErrorEventSampled = shouldSampleForBufferEvent(replay, event);
4380
+
4381
+ // Tag errors if it has been sampled in buffer mode, or if it is session mode
4382
+ // Only tag transactions if in session mode
4383
+ const shouldTagReplayId = isErrorEventSampled || replay.recordingMode === 'session';
4384
+
4385
+ if (shouldTagReplayId) {
4299
4386
  event.tags = { ...event.tags, replayId: replay.getSessionId() };
4300
4387
  }
4301
4388
 
@@ -4345,7 +4432,7 @@ function createPerformanceSpans(
4345
4432
  ) {
4346
4433
  return entries.map(({ type, start, end, name, data }) =>
4347
4434
  addEvent(replay, {
4348
- type: EventType.Custom,
4435
+ type: EventType$1.Custom,
4349
4436
  timestamp: start,
4350
4437
  data: {
4351
4438
  tag: 'performanceSpan',
@@ -5958,6 +6045,23 @@ function debounce(func, wait, options) {
5958
6045
  return debounced;
5959
6046
  }
5960
6047
 
6048
+ /* eslint-disable @typescript-eslint/naming-convention */
6049
+
6050
+ var EventType; (function (EventType) {
6051
+ const DomContentLoaded = 0; EventType[EventType["DomContentLoaded"] = DomContentLoaded] = "DomContentLoaded";
6052
+ const Load = 1; EventType[EventType["Load"] = Load] = "Load";
6053
+ const FullSnapshot = 2; EventType[EventType["FullSnapshot"] = FullSnapshot] = "FullSnapshot";
6054
+ const IncrementalSnapshot = 3; EventType[EventType["IncrementalSnapshot"] = IncrementalSnapshot] = "IncrementalSnapshot";
6055
+ const Meta = 4; EventType[EventType["Meta"] = Meta] = "Meta";
6056
+ const Custom = 5; EventType[EventType["Custom"] = Custom] = "Custom";
6057
+ const Plugin = 6; EventType[EventType["Plugin"] = Plugin] = "Plugin";
6058
+ })(EventType || (EventType = {}));
6059
+
6060
+ /**
6061
+ * This is a partial copy of rrweb's eventWithTime type which only contains the properties
6062
+ * we specifcally need in the SDK.
6063
+ */
6064
+
5961
6065
  /**
5962
6066
  * Handler for recording events.
5963
6067
  *
@@ -6000,6 +6104,14 @@ function getHandleRecordingEmit(replay) {
6000
6104
  return false;
6001
6105
  }
6002
6106
 
6107
+ // Additionally, create a meta event that will capture certain SDK settings.
6108
+ // In order to handle buffer mode, this needs to either be done when we
6109
+ // receive checkout events or at flush time.
6110
+ //
6111
+ // `isCheckout` is always true, but want to be explicit that it should
6112
+ // only be added for checkouts
6113
+ void addSettingsEvent(replay, isCheckout);
6114
+
6003
6115
  // If there is a previousSessionId after a full snapshot occurs, then
6004
6116
  // the replay session was started due to session expiration. The new session
6005
6117
  // is started before triggering a new checkout and contains the id
@@ -6037,6 +6149,46 @@ function getHandleRecordingEmit(replay) {
6037
6149
  };
6038
6150
  }
6039
6151
 
6152
+ /**
6153
+ * Exported for tests
6154
+ */
6155
+ function createOptionsEvent(replay) {
6156
+ const options = replay.getOptions();
6157
+ return {
6158
+ type: EventType.Custom,
6159
+ timestamp: Date.now(),
6160
+ data: {
6161
+ tag: 'options',
6162
+ payload: {
6163
+ sessionSampleRate: options.sessionSampleRate,
6164
+ errorSampleRate: options.errorSampleRate,
6165
+ useCompressionOption: options.useCompression,
6166
+ blockAllMedia: options.blockAllMedia,
6167
+ maskAllText: options.maskAllText,
6168
+ maskAllInputs: options.maskAllInputs,
6169
+ useCompression: replay.eventBuffer ? replay.eventBuffer.type === 'worker' : false,
6170
+ networkDetailHasUrls: options.networkDetailAllowUrls.length > 0,
6171
+ networkCaptureBodies: options.networkCaptureBodies,
6172
+ networkRequestHasHeaders: options.networkRequestHeaders.length > 0,
6173
+ networkResponseHasHeaders: options.networkResponseHeaders.length > 0,
6174
+ },
6175
+ },
6176
+ };
6177
+ }
6178
+
6179
+ /**
6180
+ * Add a "meta" event that contains a simplified view on current configuration
6181
+ * options. This should only be included on the first segment of a recording.
6182
+ */
6183
+ function addSettingsEvent(replay, isCheckout) {
6184
+ // Only need to add this event when sending the first segment
6185
+ if (!isCheckout || !replay.session || replay.session.segmentId !== 0) {
6186
+ return Promise.resolve(null);
6187
+ }
6188
+
6189
+ return addEvent(replay, createOptionsEvent(replay), false);
6190
+ }
6191
+
6040
6192
  /**
6041
6193
  * Create a replay envelope ready to be sent.
6042
6194
  * This includes both the replay event, as well as the recording data.
@@ -6151,7 +6303,6 @@ async function sendReplayRequest({
6151
6303
  eventContext,
6152
6304
  timestamp,
6153
6305
  session,
6154
- options,
6155
6306
  }) {
6156
6307
  const preparedRecordingData = prepareRecordingData({
6157
6308
  recordingData,
@@ -6193,15 +6344,6 @@ async function sendReplayRequest({
6193
6344
  return;
6194
6345
  }
6195
6346
 
6196
- replayEvent.contexts = {
6197
- ...replayEvent.contexts,
6198
- replay: {
6199
- ...(replayEvent.contexts && replayEvent.contexts.replay),
6200
- session_sample_rate: options.sessionSampleRate,
6201
- error_sample_rate: options.errorSampleRate,
6202
- },
6203
- };
6204
-
6205
6347
  /*
6206
6348
  For reference, the fully built event looks something like this:
6207
6349
  {
@@ -6232,10 +6374,6 @@ async function sendReplayRequest({
6232
6374
  },
6233
6375
  "sdkProcessingMetadata": {},
6234
6376
  "contexts": {
6235
- "replay": {
6236
- "session_sample_rate": 1,
6237
- "error_sample_rate": 0,
6238
- },
6239
6377
  },
6240
6378
  }
6241
6379
  */
@@ -6428,7 +6566,7 @@ class ReplayContainer {
6428
6566
  recordingOptions,
6429
6567
  }
6430
6568
 
6431
- ) {ReplayContainer.prototype.__init.call(this);ReplayContainer.prototype.__init2.call(this);ReplayContainer.prototype.__init3.call(this);ReplayContainer.prototype.__init4.call(this);ReplayContainer.prototype.__init5.call(this);ReplayContainer.prototype.__init6.call(this);ReplayContainer.prototype.__init7.call(this);ReplayContainer.prototype.__init8.call(this);ReplayContainer.prototype.__init9.call(this);ReplayContainer.prototype.__init10.call(this);ReplayContainer.prototype.__init11.call(this);ReplayContainer.prototype.__init12.call(this);ReplayContainer.prototype.__init13.call(this);ReplayContainer.prototype.__init14.call(this);ReplayContainer.prototype.__init15.call(this);ReplayContainer.prototype.__init16.call(this);ReplayContainer.prototype.__init17.call(this);
6569
+ ) {ReplayContainer.prototype.__init.call(this);ReplayContainer.prototype.__init2.call(this);ReplayContainer.prototype.__init3.call(this);ReplayContainer.prototype.__init4.call(this);ReplayContainer.prototype.__init5.call(this);ReplayContainer.prototype.__init6.call(this);ReplayContainer.prototype.__init7.call(this);ReplayContainer.prototype.__init8.call(this);ReplayContainer.prototype.__init9.call(this);ReplayContainer.prototype.__init10.call(this);ReplayContainer.prototype.__init11.call(this);ReplayContainer.prototype.__init12.call(this);ReplayContainer.prototype.__init13.call(this);ReplayContainer.prototype.__init14.call(this);ReplayContainer.prototype.__init15.call(this);ReplayContainer.prototype.__init16.call(this);ReplayContainer.prototype.__init17.call(this);ReplayContainer.prototype.__init18.call(this);
6432
6570
  this._recordingOptions = recordingOptions;
6433
6571
  this._options = options;
6434
6572
 
@@ -6920,6 +7058,7 @@ class ReplayContainer {
6920
7058
  WINDOW.document.addEventListener('visibilitychange', this._handleVisibilityChange);
6921
7059
  WINDOW.addEventListener('blur', this._handleWindowBlur);
6922
7060
  WINDOW.addEventListener('focus', this._handleWindowFocus);
7061
+ WINDOW.addEventListener('keydown', this._handleKeyboardEvent);
6923
7062
 
6924
7063
  // There is no way to remove these listeners, so ensure they are only added once
6925
7064
  if (!this._hasInitializedCoreListeners) {
@@ -6948,6 +7087,7 @@ class ReplayContainer {
6948
7087
 
6949
7088
  WINDOW.removeEventListener('blur', this._handleWindowBlur);
6950
7089
  WINDOW.removeEventListener('focus', this._handleWindowFocus);
7090
+ WINDOW.removeEventListener('keydown', this._handleKeyboardEvent);
6951
7091
 
6952
7092
  if (this._performanceObserver) {
6953
7093
  this._performanceObserver.disconnect();
@@ -6998,6 +7138,11 @@ class ReplayContainer {
6998
7138
  this._doChangeToForegroundTasks(breadcrumb);
6999
7139
  };}
7000
7140
 
7141
+ /** Ensure page remains active when a key is pressed. */
7142
+ __init16() {this._handleKeyboardEvent = () => {
7143
+ this.triggerUserActivity();
7144
+ };}
7145
+
7001
7146
  /**
7002
7147
  * Tasks to run when we consider a page to be hidden (via blurring and/or visibility)
7003
7148
  */
@@ -7077,7 +7222,7 @@ class ReplayContainer {
7077
7222
  _createCustomBreadcrumb(breadcrumb) {
7078
7223
  this.addUpdate(() => {
7079
7224
  void addEvent(this, {
7080
- type: EventType.Custom,
7225
+ type: EventType$1.Custom,
7081
7226
  timestamp: breadcrumb.timestamp || 0,
7082
7227
  data: {
7083
7228
  tag: 'breadcrumb',
@@ -7228,7 +7373,7 @@ class ReplayContainer {
7228
7373
  * Flush recording data to Sentry. Creates a lock so that only a single flush
7229
7374
  * can be active at a time. Do not call this directly.
7230
7375
  */
7231
- __init16() {this._flush = async ({
7376
+ __init17() {this._flush = async ({
7232
7377
  force = false,
7233
7378
  }
7234
7379
 
@@ -7283,7 +7428,7 @@ class ReplayContainer {
7283
7428
  }
7284
7429
 
7285
7430
  /** Handler for rrweb.record.onMutation */
7286
- __init17() {this._onMutationHandler = (mutations) => {
7431
+ __init18() {this._onMutationHandler = (mutations) => {
7287
7432
  const count = mutations.length;
7288
7433
 
7289
7434
  const mutationLimit = this._options._experiments.mutationLimit || 0;
@@ -7522,6 +7667,8 @@ class Replay {
7522
7667
  errorSampleRate,
7523
7668
  useCompression,
7524
7669
  blockAllMedia,
7670
+ maskAllInputs,
7671
+ maskAllText,
7525
7672
  networkDetailAllowUrls,
7526
7673
  networkCaptureBodies,
7527
7674
  networkRequestHeaders: _getMergedNetworkHeaders(networkRequestHeaders),