posthog-js 1.16.0 → 1.16.4

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/dist/es.js CHANGED
@@ -863,9 +863,9 @@ var LZString = {
863
863
  }
864
864
  };
865
865
 
866
- var version = "1.16.0";
866
+ var version = "1.16.4";
867
867
 
868
- var Config = {
868
+ var Config$1 = {
869
869
  DEBUG: false,
870
870
  LIB_VERSION: version
871
871
  };
@@ -903,7 +903,7 @@ var _ = {
903
903
  var console$1 = {
904
904
  /** @type {function(...*)} */
905
905
  log: function log() {
906
- if (Config.DEBUG && !_.isUndefined(window.console) && window.console) {
906
+ if (Config$1.DEBUG && !_.isUndefined(window.console) && window.console) {
907
907
  try {
908
908
  window.console.log.apply(window.console, arguments);
909
909
  } catch (err) {
@@ -916,7 +916,7 @@ var console$1 = {
916
916
 
917
917
  /** @type {function(...*)} */
918
918
  error: function error() {
919
- if (Config.DEBUG && !_.isUndefined(window.console) && window.console) {
919
+ if (Config$1.DEBUG && !_.isUndefined(window.console) && window.console) {
920
920
  var args = ['PostHog error:'].concat(Array.prototype.slice.call(arguments));
921
921
 
922
922
  try {
@@ -1140,7 +1140,7 @@ _.safewrap = function (f) {
1140
1140
  } catch (e) {
1141
1141
  console$1.critical('Implementation error. Please turn on debug and contact support@posthog.com.');
1142
1142
 
1143
- if (Config.DEBUG) {
1143
+ if (Config$1.DEBUG) {
1144
1144
  console$1.critical(e);
1145
1145
  }
1146
1146
  }
@@ -1734,7 +1734,7 @@ _.info = {
1734
1734
  $viewport_height: window.innerHeight,
1735
1735
  $viewport_width: window.innerWidth,
1736
1736
  $lib: 'web',
1737
- $lib_version: Config.LIB_VERSION,
1737
+ $lib_version: Config$1.LIB_VERSION,
1738
1738
  $insert_id: Math.random().toString(36).substring(2, 10) + Math.random().toString(36).substring(2, 10),
1739
1739
  $time: _.timestamp() / 1000 // epoch time in seconds
1740
1740
 
@@ -2006,16 +2006,16 @@ function shouldCaptureValue(value) {
2006
2006
  return true;
2007
2007
  }
2008
2008
  /*
2009
- * Check whether an attribute name is an Angular content attr
2009
+ * Check whether an attribute name is an Angular style attr (either _ngcontent or _nghost)
2010
2010
  * These update on each build and lead to noise in the element chain
2011
- * https://stackoverflow.com/questions/45082129/what-does-ngcontent-c-mean-in-angular
2011
+ * More details on the attributes here: https://angular.io/guide/view-encapsulation
2012
2012
  * @param {string} attributeName - string value to check
2013
2013
  * @returns {boolean} whether the element is an angular tag
2014
2014
  */
2015
2015
 
2016
- function isAngularContentAttr(attributeName) {
2016
+ function isAngularStyleAttr(attributeName) {
2017
2017
  if (typeof attributeName === 'string') {
2018
- return attributeName.substring(0, 10) === '_ngcontent';
2018
+ return attributeName.substring(0, 10) === '_ngcontent' || attributeName.substring(0, 7) === '_nghost';
2019
2019
  }
2020
2020
 
2021
2021
  return false;
@@ -2116,7 +2116,7 @@ var autocapture = {
2116
2116
  // Only capture attributes we know are safe
2117
2117
  if (isSensitiveElement(elem) && ['name', 'id', 'class'].indexOf(attr.name) === -1) return;
2118
2118
 
2119
- if (!maskInputs && shouldCaptureValue(attr.value) && !isAngularContentAttr(attr.name)) {
2119
+ if (!maskInputs && shouldCaptureValue(attr.value) && !isAngularStyleAttr(attr.name)) {
2120
2120
  props['attr__' + attr.name] = attr.value;
2121
2121
  }
2122
2122
  });
@@ -2537,6 +2537,73 @@ var memoryStore = {
2537
2537
  remove: function remove(name) {
2538
2538
  delete memoryStorage[name];
2539
2539
  }
2540
+ }; // Storage that only lasts the length of a tab/window. Survives page refreshes
2541
+
2542
+ var sessionStore = {
2543
+ sessionStorageSupported: null,
2544
+ is_supported: function is_supported() {
2545
+ if (sessionStore.sessionStorageSupported !== null) {
2546
+ return sessionStore.sessionStorageSupported;
2547
+ }
2548
+
2549
+ sessionStore.sessionStorageSupported = true;
2550
+
2551
+ if (window) {
2552
+ try {
2553
+ var key = '__support__',
2554
+ val = 'xyz';
2555
+ sessionStore.set(key, val);
2556
+
2557
+ if (sessionStore.get(key) !== '"xyz"') {
2558
+ sessionStore.sessionStorageSupported = false;
2559
+ }
2560
+
2561
+ sessionStore.remove(key);
2562
+ } catch (err) {
2563
+ sessionStore.sessionStorageSupported = false;
2564
+ }
2565
+ } else {
2566
+ sessionStore.sessionStorageSupported = false;
2567
+ }
2568
+
2569
+ return sessionStore.sessionStorageSupported;
2570
+ },
2571
+ error: function error(msg) {
2572
+ if (Config.DEBUG) {
2573
+ console$1.error('sessionStorage error: ', msg);
2574
+ }
2575
+ },
2576
+ get: function get(name) {
2577
+ try {
2578
+ return window.sessionStorage.getItem(name);
2579
+ } catch (err) {
2580
+ sessionStore.error(err);
2581
+ }
2582
+
2583
+ return null;
2584
+ },
2585
+ parse: function parse(name) {
2586
+ try {
2587
+ return JSON.parse(sessionStore.get(name)) || null;
2588
+ } catch (err) {// noop
2589
+ }
2590
+
2591
+ return null;
2592
+ },
2593
+ set: function set(name, value) {
2594
+ try {
2595
+ window.sessionStorage.setItem(name, JSON.stringify(value));
2596
+ } catch (err) {
2597
+ sessionStore.error(err);
2598
+ }
2599
+ },
2600
+ remove: function remove(name) {
2601
+ try {
2602
+ window.sessionStorage.removeItem(name);
2603
+ } catch (err) {
2604
+ sessionStore.error(err);
2605
+ }
2606
+ }
2540
2607
  };
2541
2608
 
2542
2609
  /**
@@ -3512,28 +3579,6 @@ PostHogPersistence.prototype.remove_event_timer = function (event_name) {
3512
3579
  return timestamp;
3513
3580
  };
3514
3581
 
3515
- var SESSION_CHANGE_THRESHOLD = 30 * 60 * 1000; // 30 mins
3516
-
3517
- var sessionIdGenerator = (function (persistence, timestamp) {
3518
- var _ref = persistence['props'][SESSION_ID] || [0, null],
3519
- _ref2 = _slicedToArray(_ref, 2),
3520
- lastTimestamp = _ref2[0],
3521
- sessionId = _ref2[1];
3522
-
3523
- var isNewSessionId = false;
3524
-
3525
- if (Math.abs(timestamp - lastTimestamp) > SESSION_CHANGE_THRESHOLD) {
3526
- sessionId = _.UUID();
3527
- isNewSessionId = true;
3528
- }
3529
-
3530
- persistence.register(_defineProperty({}, SESSION_ID, [timestamp, sessionId]));
3531
- return {
3532
- isNewSessionId: isNewSessionId,
3533
- sessionId: sessionId
3534
- };
3535
- });
3536
-
3537
3582
  var replacementImageURI = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2IiBmaWxsPSJibGFjayIvPgo8cGF0aCBkPSJNOCAwSDE2TDAgMTZWOEw4IDBaIiBmaWxsPSIjMkQyRDJEIi8+CjxwYXRoIGQ9Ik0xNiA4VjE2SDhMMTYgOFoiIGZpbGw9IiMyRDJEMkQiLz4KPC9zdmc+Cg==';
3538
3583
  /*
3539
3584
  * Check whether a data payload is nearing 5mb. If it is, it checks the data for
@@ -3585,6 +3630,10 @@ function filterDataURLsFromLargeDataObjects(data) {
3585
3630
  }
3586
3631
 
3587
3632
  var BASE_ENDPOINT = '/e/';
3633
+ var FULL_SNAPSHOT_EVENT_TYPE = 2;
3634
+ var META_EVENT_TYPE = 4;
3635
+ var INCREMENTAL_SNAPSHOT_EVENT_TYPE = 3;
3636
+ var MUTATION_SOURCE_TYPE = 3;
3588
3637
  var SessionRecording = /*#__PURE__*/function () {
3589
3638
  function SessionRecording(instance) {
3590
3639
  _classCallCheck(this, SessionRecording);
@@ -3595,6 +3644,8 @@ var SessionRecording = /*#__PURE__*/function () {
3595
3644
  this.emit = false;
3596
3645
  this.endpoint = BASE_ENDPOINT;
3597
3646
  this.stopRrweb = null;
3647
+ this.windowId = null;
3648
+ this.sessionId = null;
3598
3649
  }
3599
3650
 
3600
3651
  _createClass(SessionRecording, [{
@@ -3664,9 +3715,24 @@ var SessionRecording = /*#__PURE__*/function () {
3664
3715
 
3665
3716
  if (!this.captureStarted && !this.instance.get_config('disable_session_recording')) {
3666
3717
  this.captureStarted = true;
3667
- loadScript(this.instance.get_config('api_host') + '/static/recorder.js?v=' + Config.LIB_VERSION, _.bind(this._onScriptLoaded, this));
3718
+ loadScript(this.instance.get_config('api_host') + '/static/recorder.js?v=' + Config$1.LIB_VERSION, _.bind(this._onScriptLoaded, this));
3668
3719
  }
3669
3720
  }
3721
+ }, {
3722
+ key: "_updateWindowAndSessionIds",
3723
+ value: function _updateWindowAndSessionIds(event) {
3724
+ var _this$instance$_sessi = this.instance._sessionIdManager.getSessionAndWindowId(event.timestamp || new Date().getTime(), event),
3725
+ windowId = _this$instance$_sessi.windowId,
3726
+ sessionId = _this$instance$_sessi.sessionId; // Event types FullSnapshot and Meta mean we're already in the process of sending a full snapshot
3727
+
3728
+
3729
+ if ((this.windowId !== windowId || this.sessionId !== sessionId) && [FULL_SNAPSHOT_EVENT_TYPE, META_EVENT_TYPE].indexOf(event.type) === -1) {
3730
+ window.rrweb.record.takeFullSnapshot();
3731
+ }
3732
+
3733
+ this.windowId = windowId;
3734
+ this.sessionId = sessionId;
3735
+ }
3670
3736
  }, {
3671
3737
  key: "_onScriptLoaded",
3672
3738
  value: function _onScriptLoaded() {
@@ -3699,23 +3765,20 @@ var SessionRecording = /*#__PURE__*/function () {
3699
3765
  }
3700
3766
 
3701
3767
  this.stopRrweb = window.rrweb.record(_objectSpread2({
3702
- emit: function emit(data) {
3703
- data = filterDataURLsFromLargeDataObjects(data);
3704
- var sessionIdObject = sessionIdGenerator(_this2.instance.persistence, data.timestamp); // Data type 2 and 4 are FullSnapshot and Meta and they mean we're already
3705
- // in the process of sending a full snapshot
3768
+ emit: function emit(event) {
3769
+ event = filterDataURLsFromLargeDataObjects(event);
3706
3770
 
3707
- if (sessionIdObject.isNewSessionId && [2, 4].indexOf(data.type) === -1) {
3708
- window.rrweb.record.takeFullSnapshot();
3709
- }
3771
+ _this2._updateWindowAndSessionIds(event);
3710
3772
 
3711
3773
  var properties = {
3712
- $snapshot_data: data,
3713
- $session_id: sessionIdObject.sessionId
3774
+ $snapshot_data: event,
3775
+ $session_id: _this2.sessionId,
3776
+ $window_id: _this2.windowId
3714
3777
  };
3715
3778
 
3716
3779
  _this2.instance._captureMetrics.incr('rrweb-record');
3717
3780
 
3718
- _this2.instance._captureMetrics.incr("rrweb-record-".concat(data.type));
3781
+ _this2.instance._captureMetrics.incr("rrweb-record-".concat(event.type));
3719
3782
 
3720
3783
  if (_this2.emit) {
3721
3784
  _this2._captureSnapshot(properties);
@@ -3746,7 +3809,7 @@ var SessionRecording = /*#__PURE__*/function () {
3746
3809
  _noTruncate: true,
3747
3810
  _batchKey: 'sessionRecording',
3748
3811
  _metrics: {
3749
- rrweb_full_snapshot: properties.$snapshot_data.type === 2
3812
+ rrweb_full_snapshot: properties.$snapshot_data.type === FULL_SNAPSHOT_EVENT_TYPE
3750
3813
  }
3751
3814
  });
3752
3815
  }
@@ -3935,6 +3998,10 @@ var Toolbar = /*#__PURE__*/function () {
3935
3998
  var host = editorParams['jsURL'] || editorParams['apiURL'] || this.instance.get_config('api_host');
3936
3999
  var toolbarScript = 'toolbar.js';
3937
4000
  var editorUrl = host + (host.endsWith('/') ? '' : '/') + 'static/' + toolbarScript + '?_ts=' + new Date().getTime();
4001
+ var disableToolbarMetrics = this.instance.get_config('api_host') !== 'https://app.posthog.com' && this.instance.get_config('advanced_disable_toolbar_metrics');
4002
+ editorParams = _objectSpread2(_objectSpread2({}, editorParams), disableToolbarMetrics ? {
4003
+ instrument: false
4004
+ } : {});
3938
4005
  loadScript(editorUrl, function () {
3939
4006
  window['ph_load_editor'](editorParams, _this.instance);
3940
4007
  }); // Turbolinks doesn't fire an onload event but does replace the entire page, including the toolbar
@@ -5316,6 +5383,112 @@ var RetryQueue = /*#__PURE__*/function (_RequestQueueScaffold) {
5316
5383
  return RetryQueue;
5317
5384
  }(RequestQueueScaffold);
5318
5385
 
5386
+ var SESSION_CHANGE_THRESHOLD = 30 * 60 * 1000; // 30 mins
5387
+
5388
+ var SessionIdManager = /*#__PURE__*/function () {
5389
+ function SessionIdManager(config, persistence) {
5390
+ _classCallCheck(this, SessionIdManager);
5391
+
5392
+ this.persistence = persistence;
5393
+
5394
+ if (config['persistence_name']) {
5395
+ this.window_id_storage_key = 'ph_' + config['persistence_name'] + '_window_id';
5396
+ } else {
5397
+ this.window_id_storage_key = 'ph_' + config['token'] + '_window_id';
5398
+ }
5399
+ } // Note: this tries to store the windowId in sessionStorage. SessionStorage is unique to the current window/tab,
5400
+ // and persists page loads/reloads. So it's uniquely suited for storing the windowId. This function also respects
5401
+ // when persistence is disabled (by user config) and when sessionStorage is not supported (it *should* be supported on all browsers),
5402
+ // and in that case, it falls back to memory (which sadly, won't persist page loads)
5403
+
5404
+
5405
+ _createClass(SessionIdManager, [{
5406
+ key: "_setWindowId",
5407
+ value: function _setWindowId(windowId) {
5408
+ if (windowId !== this.windowId) {
5409
+ this.windowId = windowId;
5410
+
5411
+ if (!this.persistence.disabled && sessionStore.is_supported()) {
5412
+ sessionStore.set(this.window_id_storage_key, windowId);
5413
+ }
5414
+ }
5415
+ }
5416
+ }, {
5417
+ key: "_getWindowId",
5418
+ value: function _getWindowId() {
5419
+ if (this.windowId) {
5420
+ return this.windowId;
5421
+ }
5422
+
5423
+ if (!this.persistence.disabled && sessionStore.is_supported()) {
5424
+ return sessionStore.parse(this.window_id_storage_key);
5425
+ }
5426
+
5427
+ return null;
5428
+ } // Note: 'this.persistence.register' can be disabled in the config.
5429
+ // In that case, this works by storing sessionId and the timestamp in memory.
5430
+
5431
+ }, {
5432
+ key: "_setSessionId",
5433
+ value: function _setSessionId(sessionId, timestamp) {
5434
+ if (sessionId !== this.sessionId || timestamp !== this.timestamp) {
5435
+ this.timestamp = timestamp;
5436
+ this.sessionId = sessionId;
5437
+ this.persistence.register(_defineProperty({}, SESSION_ID, [timestamp, sessionId]));
5438
+ }
5439
+ }
5440
+ }, {
5441
+ key: "_getSessionId",
5442
+ value: function _getSessionId() {
5443
+ if (this.sessionId && this.timestamp) {
5444
+ return [this.timestamp, this.sessionId];
5445
+ }
5446
+
5447
+ return this.persistence['props'][SESSION_ID] || [0, null];
5448
+ }
5449
+ }, {
5450
+ key: "getSessionAndWindowId",
5451
+ value: function getSessionAndWindowId() {
5452
+ var _recordingEvent$data;
5453
+
5454
+ var timestamp = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
5455
+ var recordingEvent = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
5456
+ // Some recording events are triggered by non-user events (e.g. "X minutes ago" text updating on the screen).
5457
+ // We don't want to update the session and window ids in these cases. These events are designated by event
5458
+ // type -> incremental update, and source -> mutation.
5459
+ var isUserInteraction = !(recordingEvent && recordingEvent.type === INCREMENTAL_SNAPSHOT_EVENT_TYPE && ((_recordingEvent$data = recordingEvent.data) === null || _recordingEvent$data === void 0 ? void 0 : _recordingEvent$data.source) === MUTATION_SOURCE_TYPE);
5460
+ timestamp = timestamp || new Date().getTime();
5461
+
5462
+ var _this$_getSessionId = this._getSessionId(),
5463
+ _this$_getSessionId2 = _slicedToArray(_this$_getSessionId, 2),
5464
+ lastTimestamp = _this$_getSessionId2[0],
5465
+ sessionId = _this$_getSessionId2[1];
5466
+
5467
+ var windowId = this._getWindowId();
5468
+
5469
+ if (!sessionId || isUserInteraction && Math.abs(timestamp - lastTimestamp) > SESSION_CHANGE_THRESHOLD) {
5470
+ sessionId = _.UUID();
5471
+ windowId = _.UUID();
5472
+ } else if (!windowId) {
5473
+ windowId = _.UUID();
5474
+ }
5475
+
5476
+ var newTimestamp = lastTimestamp === 0 || isUserInteraction ? timestamp : lastTimestamp;
5477
+
5478
+ this._setWindowId(windowId);
5479
+
5480
+ this._setSessionId(sessionId, newTimestamp);
5481
+
5482
+ return {
5483
+ sessionId: sessionId,
5484
+ windowId: windowId
5485
+ };
5486
+ }
5487
+ }]);
5488
+
5489
+ return SessionIdManager;
5490
+ }();
5491
+
5319
5492
  /*
5320
5493
  SIMPLE STYLE GUIDE:
5321
5494
 
@@ -5402,6 +5575,7 @@ var defaultConfig = function defaultConfig() {
5402
5575
  mask_all_element_attributes: false,
5403
5576
  mask_all_text: false,
5404
5577
  advanced_disable_decide: false,
5578
+ advanced_disable_toolbar_metrics: false,
5405
5579
  on_xhr_error: function on_xhr_error(req) {
5406
5580
  var error = 'Bad HTTP status: ' + req.status + ' ' + req.statusText;
5407
5581
  console$1.error(error);
@@ -5479,7 +5653,7 @@ var create_mplib = function create_mplib(token, config, name) {
5479
5653
  // global debug to be true
5480
5654
 
5481
5655
 
5482
- Config.DEBUG = Config.DEBUG || instance.get_config('debug'); // if target is not defined, we called init after the lib already
5656
+ Config$1.DEBUG = Config$1.DEBUG || instance.get_config('debug'); // if target is not defined, we called init after the lib already
5483
5657
  // loaded, so there won't be an array of things to execute
5484
5658
 
5485
5659
  if (!_.isUndefined(target) && _.isArray(target)) {
@@ -5556,6 +5730,7 @@ PostHogLib.prototype._init = function (token, config, name) {
5556
5730
  this.__captureHooks = [];
5557
5731
  this.__request_queue = [];
5558
5732
  this['persistence'] = new PostHogPersistence(this['config']);
5733
+ this['_sessionIdManager'] = new SessionIdManager(this['config'], this['persistence']);
5559
5734
 
5560
5735
  this._gdpr_init();
5561
5736
 
@@ -5947,6 +6122,15 @@ PostHogLib.prototype._calculate_event_properties = function (event_name, event_p
5947
6122
  if (!_.isUndefined(start_timestamp)) {
5948
6123
  var duration_in_ms = new Date().getTime() - start_timestamp;
5949
6124
  properties['$duration'] = parseFloat((duration_in_ms / 1000).toFixed(3));
6125
+ }
6126
+
6127
+ if (this._sessionIdManager) {
6128
+ var _this$_sessionIdManag = this._sessionIdManager.getSessionAndWindowId(),
6129
+ sessionId = _this$_sessionIdManag.sessionId,
6130
+ windowId = _this$_sessionIdManag.windowId;
6131
+
6132
+ properties['$session_id'] = sessionId;
6133
+ properties['$window_id'] = windowId;
5950
6134
  } // note: extend writes to the first object, so lets make sure we
5951
6135
  // don't write to the persistence properties object and info
5952
6136
  // properties object by passing in a new object
@@ -6462,7 +6646,7 @@ PostHogLib.prototype.set_config = function (config) {
6462
6646
  this['config']['debug'] = true;
6463
6647
  }
6464
6648
 
6465
- Config.DEBUG = Config.DEBUG || this.get_config('debug');
6649
+ Config$1.DEBUG = Config$1.DEBUG || this.get_config('debug');
6466
6650
 
6467
6651
  if (this.sessionRecording && typeof config.disable_session_recording !== 'undefined') {
6468
6652
  if (oldConfig.disable_session_recording !== config.disable_session_recording) {
@@ -6874,7 +7058,7 @@ PostHogLib.prototype['onFeatureFlags'] = PostHogLib.prototype.onFeatureFlags;
6874
7058
  PostHogLib.prototype['decodeLZ64'] = PostHogLib.prototype.decodeLZ64;
6875
7059
  PostHogLib.prototype['SentryIntegration'] = PostHogLib.prototype.sentry_integration;
6876
7060
  PostHogLib.prototype['debug'] = PostHogLib.prototype.debug;
6877
- PostHogLib.prototype['LIB_VERSION'] = Config.LIB_VERSION;
7061
+ PostHogLib.prototype['LIB_VERSION'] = Config$1.LIB_VERSION;
6878
7062
  PostHogLib.prototype['startSessionRecording'] = PostHogLib.prototype.startSessionRecording;
6879
7063
  PostHogLib.prototype['stopSessionRecording'] = PostHogLib.prototype.stopSessionRecording;
6880
7064
  PostHogLib.prototype['sessionRecordingStarted'] = PostHogLib.prototype.sessionRecordingStarted; // PostHogPersistence Exports
package/dist/module.d.ts CHANGED
@@ -294,6 +294,12 @@ declare class posthog {
294
294
  * // autocapture, feature flags, compression and session recording will be disabled when set to `true`
295
295
  * advanced_disable_decide: false
296
296
  *
297
+ *
298
+ * // disable capturing of metrics about toolbar usage
299
+ * // only available to self-hosted users, changing this
300
+ * // setting will not do anything if you use PostHog Cloud
301
+ * advanced_disable_toolbar_metrics: false
302
+ *
297
303
  * }
298
304
  *
299
305
  *
@@ -605,6 +611,7 @@ declare namespace posthog {
605
611
  mask_all_element_attributes?: boolean
606
612
  mask_all_text?: boolean
607
613
  advanced_disable_decide?: boolean
614
+ advanced_disable_toolbar_metrics?: boolean
608
615
  }
609
616
 
610
617
  interface OptInOutCapturingOptions {