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