mixpanel-browser 2.71.1 → 2.72.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.
Files changed (38) hide show
  1. package/.github/workflows/tests.yml +1 -0
  2. package/CHANGELOG.md +7 -0
  3. package/dist/mixpanel-core.cjs.d.ts +47 -10
  4. package/dist/mixpanel-core.cjs.js +51 -17
  5. package/dist/mixpanel-recorder.js +681 -113
  6. package/dist/mixpanel-recorder.min.js +1 -1
  7. package/dist/mixpanel-recorder.min.js.map +1 -1
  8. package/dist/mixpanel-with-async-recorder.cjs.d.ts +47 -10
  9. package/dist/mixpanel-with-async-recorder.cjs.js +51 -17
  10. package/dist/mixpanel-with-recorder.d.ts +47 -10
  11. package/dist/mixpanel-with-recorder.js +731 -129
  12. package/dist/mixpanel-with-recorder.min.d.ts +47 -10
  13. package/dist/mixpanel-with-recorder.min.js +1 -1
  14. package/dist/mixpanel.amd.d.ts +47 -10
  15. package/dist/mixpanel.amd.js +731 -129
  16. package/dist/mixpanel.cjs.d.ts +47 -10
  17. package/dist/mixpanel.cjs.js +731 -129
  18. package/dist/mixpanel.globals.js +51 -17
  19. package/dist/mixpanel.min.js +144 -144
  20. package/dist/mixpanel.module.d.ts +47 -10
  21. package/dist/mixpanel.module.js +731 -129
  22. package/dist/mixpanel.umd.d.ts +47 -10
  23. package/dist/mixpanel.umd.js +731 -129
  24. package/dist/rrweb-bundled.js +12760 -0
  25. package/dist/rrweb-compiled.js +2496 -7176
  26. package/package.json +3 -2
  27. package/rollup.config.mjs +15 -4
  28. package/src/autocapture/index.js +1 -1
  29. package/src/autocapture/rageclick.js +20 -1
  30. package/src/autocapture/shadow-dom-observer.js +3 -15
  31. package/src/autocapture/utils.js +30 -0
  32. package/src/config.js +1 -1
  33. package/src/index.d.ts +47 -10
  34. package/src/mixpanel-core.js +1 -0
  35. package/src/recorder/recorder.js +1 -1
  36. package/src/recorder/rrweb-entrypoint.js +6 -0
  37. package/src/recorder/session-recording.js +69 -12
  38. package/src/utils.js +24 -0
@@ -43,20 +43,22 @@ export interface OutTrackingOptions extends ClearOptOutInOutOptions {
43
43
  export type RageClickConfig =
44
44
  | boolean
45
45
  | {
46
- /** Distance threshold in pixels for clicks to be considered within the same area (default: 30) */
47
- threshold_px?: number;
48
- /** Time window in milliseconds for clicks to be considered rapid (default: 1000) */
49
- timeout_ms?: number;
50
- /** Number of clicks required to trigger a rage click event (default: 3) */
51
- click_count?: number;
52
- };
46
+ /** Distance threshold in pixels for clicks to be considered within the same area (default: 30) */
47
+ threshold_px?: number;
48
+ /** Time window in milliseconds for clicks to be considered rapid (default: 1000) */
49
+ timeout_ms?: number;
50
+ /** Number of clicks required to trigger a rage click event (default: 3) */
51
+ click_count?: number;
52
+ /** Whether to only track rage clicks on interactive elements like buttons, links, inputs (default: false) */
53
+ interactive_elements_only?: boolean;
54
+ };
53
55
 
54
56
  export type DeadClickConfig =
55
57
  | boolean
56
58
  | {
57
- /** Time in milliseconds to wait after a click before qualifying it as dead (default: 500) */
58
- timeout_ms?: number;
59
- };
59
+ /** Time in milliseconds to wait after a click before qualifying it as dead (default: 500) */
60
+ timeout_ms?: number;
61
+ };
60
62
 
61
63
  export interface RegisterOptions {
62
64
  persistent: boolean;
@@ -149,6 +151,10 @@ export interface AutocaptureConfig {
149
151
  block_element_callback?: (element: Element, event: Event) => boolean;
150
152
  }
151
153
 
154
+ export interface FlagsConfig {
155
+ context: Dict;
156
+ }
157
+
152
158
  export interface Config {
153
159
  api_host: string;
154
160
  api_routes: {
@@ -165,6 +171,7 @@ export interface Config {
165
171
  cookie_domain: string;
166
172
  cross_site_cookie: boolean;
167
173
  cross_subdomain_cookie: boolean;
174
+ flags: boolean | FlagsConfig;
168
175
  persistence: Persistence;
169
176
  persistence_name: string;
170
177
  cookie_name: string;
@@ -277,11 +284,41 @@ export interface Group {
277
284
  unset(prop: string, callback?: Callback): void;
278
285
  }
279
286
 
287
+ export interface FlagsVariant {
288
+ key: string;
289
+ value: any;
290
+ experiment_id?: string;
291
+ is_experiment_active?: boolean;
292
+ is_qa_tester?: boolean;
293
+ }
294
+
295
+ export interface FlagsUpdateContextOptions {
296
+ replace?: boolean;
297
+ }
298
+
299
+ export interface FlagsManager {
300
+ are_flags_ready(): boolean;
301
+ get_variant(
302
+ featureName: string,
303
+ fallback: FlagsVariant
304
+ ): Promise<FlagsVariant>;
305
+ get_variant_sync(featureName: string, fallback: FlagsVariant): FlagsVariant;
306
+ get_variant_value(featureName: string, fallbackValue: any): Promise<any>;
307
+ get_variant_value_sync(featureName: string, fallbackValue: any): any;
308
+ is_enabled(featureName: string, fallbackValue?: boolean): Promise<boolean>;
309
+ is_enabled_sync(featureName: string, fallbackValue?: boolean): boolean;
310
+ update_context(
311
+ context: Dict,
312
+ options?: FlagsUpdateContextOptions
313
+ ): Promise<void>;
314
+ }
315
+
280
316
  export interface Mixpanel {
281
317
  add_group(group_key: string, group_id: string, callback?: Callback): void;
282
318
  alias(alias: string, original?: string): void;
283
319
  clear_opt_in_out_tracking(options?: Partial<ClearOptOutInOutOptions>): void;
284
320
  disable(events?: string[]): void;
321
+ flags: FlagsManager;
285
322
  get_config(prop_name?: string): any;
286
323
  get_distinct_id(): any;
287
324
  get_group(group_key: string, group_id: string): Group;
@@ -2,7 +2,7 @@
2
2
 
3
3
  var Config = {
4
4
  DEBUG: false,
5
- LIB_VERSION: '2.71.1'
5
+ LIB_VERSION: '2.72.0'
6
6
  };
7
7
 
8
8
  // since es6 imports are static and we run unit tests from the console, window won't be defined when importing this file
@@ -2892,20 +2892,65 @@ function isDefinitelyNonInteractive(element) {
2892
2892
  return false;
2893
2893
  }
2894
2894
 
2895
+ /**
2896
+ * Get the composed path of a click event for elements embedded in shadow DOM.
2897
+ * @param {Event} event - event to get the composed path from
2898
+ * @returns {Array} the composed path of the click event
2899
+ */
2900
+ function getClickEventComposedPath(event) {
2901
+ if ('composedPath' in event) {
2902
+ return event['composedPath']();
2903
+ }
2904
+
2905
+ return [];
2906
+ }
2907
+
2908
+ /**
2909
+ * Get the element from a click event, accounting for elements embedded in shadow DOM.
2910
+ * @param {Event} event - event to get the target from
2911
+ * @returns {Element | null} the element that was the target of the click event
2912
+ */
2913
+ function getClickEventTargetElement(event) {
2914
+ var path = getClickEventComposedPath(event);
2915
+
2916
+ if (path && path.length > 0) {
2917
+ return path[0];
2918
+ }
2919
+
2920
+ return event['target'] || event['srcElement'];
2921
+ }
2922
+
2895
2923
  /** @const */ var DEFAULT_RAGE_CLICK_THRESHOLD_PX = 30;
2896
2924
  /** @const */ var DEFAULT_RAGE_CLICK_TIMEOUT_MS = 1000;
2897
2925
  /** @const */ var DEFAULT_RAGE_CLICK_CLICK_COUNT = 4;
2926
+ /** @const */ var DEFAULT_RAGE_CLICK_INTERACTIVE_ELEMENTS_ONLY = false;
2898
2927
 
2899
2928
  function RageClickTracker() {
2900
2929
  this.clicks = [];
2901
2930
  }
2902
2931
 
2903
- RageClickTracker.prototype.isRageClick = function(x, y, options) {
2932
+ /**
2933
+ * Determines if a click event is part of a rage click sequence.
2934
+ * @param {Event} event - the original click event.
2935
+ * @param {import('../index.d.ts').RageClickConfig} options - configuration options for rage click detection.
2936
+ * @returns {boolean} - true if the click is considered a rage click, false otherwise.
2937
+ */
2938
+ RageClickTracker.prototype.isRageClick = function(event, options) {
2904
2939
  options = options || {};
2905
2940
  var thresholdPx = options['threshold_px'] || DEFAULT_RAGE_CLICK_THRESHOLD_PX;
2906
2941
  var timeoutMs = options['timeout_ms'] || DEFAULT_RAGE_CLICK_TIMEOUT_MS;
2907
2942
  var clickCount = options['click_count'] || DEFAULT_RAGE_CLICK_CLICK_COUNT;
2943
+ var interactiveElementsOnly = options['interactive_elements_only'] || DEFAULT_RAGE_CLICK_INTERACTIVE_ELEMENTS_ONLY;
2944
+
2945
+ if (interactiveElementsOnly) {
2946
+ var target = getClickEventTargetElement(event);
2947
+ if (!target || isDefinitelyNonInteractive(target)) {
2948
+ return false;
2949
+ }
2950
+ }
2951
+
2908
2952
  var timestamp = Date.now();
2953
+ var x = event['pageX'], y = event['pageY'];
2909
2954
 
2910
2955
  var lastClick = this.clicks[this.clicks.length - 1];
2911
2956
  if (
@@ -2936,28 +2981,16 @@ ShadowDOMObserver.prototype.getEventTarget = function(event) {
2936
2981
  if (!this.observedShadowRoots) {
2937
2982
  return;
2938
2983
  }
2939
- var path = this.getComposedPath(event);
2940
- if (path && path.length) {
2941
- return path[0];
2942
- }
2943
2984
 
2944
- return event['target'] || event['srcElement'];
2985
+ return getClickEventTargetElement(event);
2945
2986
  };
2946
2987
 
2947
-
2948
- ShadowDOMObserver.prototype.getComposedPath = function(event) {
2949
- if ('composedPath' in event) {
2950
- return event['composedPath']();
2951
- }
2952
-
2953
- return [];
2954
- };
2955
2988
  ShadowDOMObserver.prototype.observeFromEvent = function(event) {
2956
2989
  if (!this.observedShadowRoots) {
2957
2990
  return;
2958
2991
  }
2959
2992
 
2960
- var path = this.getComposedPath(event);
2993
+ var path = getClickEventComposedPath(event);
2961
2994
 
2962
2995
  // Check each element in path for shadow roots
2963
2996
  for (var i = 0; i < path.length; i++) {
@@ -3709,7 +3742,7 @@ Autocapture.prototype.initRageClickTracking = function() {
3709
3742
  return;
3710
3743
  }
3711
3744
 
3712
- if (this._rageClickTracker.isRageClick(ev['pageX'], ev['pageY'], currentRageClickConfig)) {
3745
+ if (this._rageClickTracker.isRageClick(ev, currentRageClickConfig)) {
3713
3746
  this.trackDomEvent(ev, MP_EV_RAGE_CLICK);
3714
3747
  }
3715
3748
  }.bind(this);
@@ -6897,6 +6930,7 @@ var DEFAULT_CONFIG = {
6897
6930
  'record_block_selector': 'img, video, audio',
6898
6931
  'record_canvas': false,
6899
6932
  'record_collect_fonts': false,
6933
+ 'record_console': true,
6900
6934
  'record_heatmap_data': false,
6901
6935
  'record_idle_timeout_ms': 30 * 60 * 1000, // 30 minutes
6902
6936
  'record_mask_text_class': new RegExp('^(mp-mask|fs-mask|amp-mask|rr-mask|ph-mask)$'),
@@ -43,20 +43,22 @@ export interface OutTrackingOptions extends ClearOptOutInOutOptions {
43
43
  export type RageClickConfig =
44
44
  | boolean
45
45
  | {
46
- /** Distance threshold in pixels for clicks to be considered within the same area (default: 30) */
47
- threshold_px?: number;
48
- /** Time window in milliseconds for clicks to be considered rapid (default: 1000) */
49
- timeout_ms?: number;
50
- /** Number of clicks required to trigger a rage click event (default: 3) */
51
- click_count?: number;
52
- };
46
+ /** Distance threshold in pixels for clicks to be considered within the same area (default: 30) */
47
+ threshold_px?: number;
48
+ /** Time window in milliseconds for clicks to be considered rapid (default: 1000) */
49
+ timeout_ms?: number;
50
+ /** Number of clicks required to trigger a rage click event (default: 3) */
51
+ click_count?: number;
52
+ /** Whether to only track rage clicks on interactive elements like buttons, links, inputs (default: false) */
53
+ interactive_elements_only?: boolean;
54
+ };
53
55
 
54
56
  export type DeadClickConfig =
55
57
  | boolean
56
58
  | {
57
- /** Time in milliseconds to wait after a click before qualifying it as dead (default: 500) */
58
- timeout_ms?: number;
59
- };
59
+ /** Time in milliseconds to wait after a click before qualifying it as dead (default: 500) */
60
+ timeout_ms?: number;
61
+ };
60
62
 
61
63
  export interface RegisterOptions {
62
64
  persistent: boolean;
@@ -149,6 +151,10 @@ export interface AutocaptureConfig {
149
151
  block_element_callback?: (element: Element, event: Event) => boolean;
150
152
  }
151
153
 
154
+ export interface FlagsConfig {
155
+ context: Dict;
156
+ }
157
+
152
158
  export interface Config {
153
159
  api_host: string;
154
160
  api_routes: {
@@ -165,6 +171,7 @@ export interface Config {
165
171
  cookie_domain: string;
166
172
  cross_site_cookie: boolean;
167
173
  cross_subdomain_cookie: boolean;
174
+ flags: boolean | FlagsConfig;
168
175
  persistence: Persistence;
169
176
  persistence_name: string;
170
177
  cookie_name: string;
@@ -277,11 +284,41 @@ export interface Group {
277
284
  unset(prop: string, callback?: Callback): void;
278
285
  }
279
286
 
287
+ export interface FlagsVariant {
288
+ key: string;
289
+ value: any;
290
+ experiment_id?: string;
291
+ is_experiment_active?: boolean;
292
+ is_qa_tester?: boolean;
293
+ }
294
+
295
+ export interface FlagsUpdateContextOptions {
296
+ replace?: boolean;
297
+ }
298
+
299
+ export interface FlagsManager {
300
+ are_flags_ready(): boolean;
301
+ get_variant(
302
+ featureName: string,
303
+ fallback: FlagsVariant
304
+ ): Promise<FlagsVariant>;
305
+ get_variant_sync(featureName: string, fallback: FlagsVariant): FlagsVariant;
306
+ get_variant_value(featureName: string, fallbackValue: any): Promise<any>;
307
+ get_variant_value_sync(featureName: string, fallbackValue: any): any;
308
+ is_enabled(featureName: string, fallbackValue?: boolean): Promise<boolean>;
309
+ is_enabled_sync(featureName: string, fallbackValue?: boolean): boolean;
310
+ update_context(
311
+ context: Dict,
312
+ options?: FlagsUpdateContextOptions
313
+ ): Promise<void>;
314
+ }
315
+
280
316
  export interface Mixpanel {
281
317
  add_group(group_key: string, group_id: string, callback?: Callback): void;
282
318
  alias(alias: string, original?: string): void;
283
319
  clear_opt_in_out_tracking(options?: Partial<ClearOptOutInOutOptions>): void;
284
320
  disable(events?: string[]): void;
321
+ flags: FlagsManager;
285
322
  get_config(prop_name?: string): any;
286
323
  get_distinct_id(): any;
287
324
  get_group(group_key: string, group_id: string): Group;