mixpanel-browser 2.62.0 → 2.64.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/CHANGELOG.md +8 -0
- package/build.sh +4 -21
- package/dist/mixpanel-core.cjs.js +261 -42
- package/dist/mixpanel-js-wrapper.min.js +5 -5
- package/dist/mixpanel-jslib-snippet.min.js +3 -3
- package/dist/mixpanel-jslib-snippet.min.test.js +3 -3
- package/dist/mixpanel-recorder.js +13552 -4502
- package/dist/mixpanel-recorder.min.js +1 -38
- package/dist/mixpanel-recorder.min.js.map +1 -1
- package/dist/mixpanel-with-async-recorder.cjs.js +260 -42
- package/dist/mixpanel-with-recorder.js +22430 -0
- package/dist/mixpanel-with-recorder.min.js +1 -0
- package/dist/mixpanel.amd.js +13832 -4563
- package/dist/mixpanel.cjs.js +13832 -4563
- package/dist/mixpanel.globals.js +260 -42
- package/dist/mixpanel.min.js +150 -144
- package/dist/mixpanel.module.js +13832 -4563
- package/dist/mixpanel.umd.js +13832 -4563
- package/dist/rrweb-compiled.js +18693 -0
- package/package.json +8 -4
- package/rollup.config.mjs +223 -0
- package/src/autocapture/index.js +4 -3
- package/src/autocapture/utils.js +4 -0
- package/src/config.js +1 -1
- package/src/flags/index.js +191 -0
- package/src/loaders/loader-globals-with-recorder.js +7 -0
- package/src/mixpanel-core.js +24 -3
- package/src/recorder/session-recording.js +1 -1
- package/src/storage/local-storage.js +2 -1
- package/rollup.config.js +0 -11
- package/src/recorder/rollup.config.js +0 -20
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
**2.64.0** (15 Apr 2025)
|
|
2
|
+
- Add `record_heatmap_data` init option for Session Recording to ensure click events are captured for Heat Maps
|
|
3
|
+
- Initial support for feature flagging
|
|
4
|
+
|
|
5
|
+
**2.63.0** (1 Apr 2025)
|
|
6
|
+
- Update rrweb to latest alpha version
|
|
7
|
+
- Refactor SDK build process to rely mainly on Rollup
|
|
8
|
+
|
|
1
9
|
**2.62.0** (26 Mar 2025)
|
|
2
10
|
- Replace UUID generator with UUIDv4 (using native API when available)
|
|
3
11
|
- Consistently use native JSON serialization when available
|
package/build.sh
CHANGED
|
@@ -5,33 +5,16 @@ set -e
|
|
|
5
5
|
# building with $DIST=1 also implies $FULL=1
|
|
6
6
|
if [ ! -z "$DIST" ]; then
|
|
7
7
|
export FULL=1
|
|
8
|
+
rm -r -f build
|
|
9
|
+
npm install
|
|
8
10
|
fi
|
|
9
11
|
|
|
10
12
|
echo 'Building main bundles'
|
|
11
|
-
npx rollup -
|
|
12
|
-
|
|
13
|
+
npx rollup -c rollup.config.mjs
|
|
14
|
+
|
|
13
15
|
ln -sf mixpanel.globals.js build/mixpanel.js
|
|
14
16
|
|
|
15
17
|
if [ ! -z "$FULL" ]; then
|
|
16
|
-
echo 'Minifying main build and snippets'
|
|
17
|
-
java -jar vendor/closure-compiler/compiler.jar --js build/mixpanel.js --language_in ECMASCRIPT5 --externs src/externs.js --js_output_file build/mixpanel.min.js --compilation_level ADVANCED_OPTIMIZATIONS --output_wrapper "(function() {
|
|
18
|
-
%output%
|
|
19
|
-
})();"
|
|
20
|
-
java -jar vendor/closure-compiler/compiler.jar --js src/loaders/mixpanel-jslib-snippet.js --language_in ECMASCRIPT5 --js_output_file build/mixpanel-jslib-snippet.min.js --compilation_level ADVANCED_OPTIMIZATIONS
|
|
21
|
-
java -jar vendor/closure-compiler/compiler.jar --js src/loaders/mixpanel-jslib-snippet.js --language_in ECMASCRIPT5 --js_output_file build/mixpanel-jslib-snippet.min.test.js --compilation_level ADVANCED_OPTIMIZATIONS --define='MIXPANEL_LIB_URL="../build/mixpanel.min.js"'
|
|
22
|
-
|
|
23
|
-
echo 'Building mixpanel-js-wrapper'
|
|
24
|
-
npx rollup src/loaders/mixpanel-js-wrapper.js -o build/mixpanel-js-wrapper.js -c rollup.config.js
|
|
25
|
-
java -jar vendor/closure-compiler/compiler.jar --js build/mixpanel-js-wrapper.js --js_output_file build/mixpanel-js-wrapper.min.js --compilation_level ADVANCED_OPTIMIZATIONS
|
|
26
|
-
|
|
27
|
-
echo 'Building module bundles'
|
|
28
|
-
npx rollup -i src/loaders/loader-module.js -f amd -o build/mixpanel.amd.js -c rollup.config.js
|
|
29
|
-
npx rollup -i src/loaders/loader-module.js -f cjs -o build/mixpanel.cjs.js -c rollup.config.js
|
|
30
|
-
npx rollup -i src/loaders/loader-module.js -f es -o build/mixpanel.module.js -c rollup.config.js
|
|
31
|
-
npx rollup -i src/loaders/loader-module-core.js -f cjs -o build/mixpanel-core.cjs.js -c rollup.config.js
|
|
32
|
-
npx rollup -i src/loaders/loader-module-with-async-recorder.js -f cjs -o build/mixpanel-with-async-recorder.cjs.js -c rollup.config.js
|
|
33
|
-
npx rollup -i src/loaders/loader-module.js -f umd -o build/mixpanel.umd.js -n mixpanel -c rollup.config.js
|
|
34
|
-
|
|
35
18
|
echo 'Bundling module-loader test runners'
|
|
36
19
|
npx webpack tests/module-cjs.js tests/module-cjs.bundle.js
|
|
37
20
|
npx browserify tests/module-es2015.js -t [ babelify --compact false ] --outfile tests/module-es2015.bundle.js
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var Config = {
|
|
4
4
|
DEBUG: false,
|
|
5
|
-
LIB_VERSION: '2.
|
|
5
|
+
LIB_VERSION: '2.64.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
|
|
@@ -1238,7 +1238,7 @@ _.UUID = function() {
|
|
|
1238
1238
|
uuid[i] = Math.floor(Math.random() * 16);
|
|
1239
1239
|
}
|
|
1240
1240
|
uuid[14] = 4; // set bits 12-15 of time-high-and-version to 0100
|
|
1241
|
-
uuid[19] = uuid[19] &=
|
|
1241
|
+
uuid[19] = uuid[19] &= -5; // set bit 6 of clock-seq-and-reserved to zero
|
|
1242
1242
|
uuid[19] = uuid[19] |= (1 << 3); // set bit 7 of clock-seq-and-reserved to one
|
|
1243
1243
|
uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
|
|
1244
1244
|
|
|
@@ -2131,6 +2131,8 @@ var isRecordingExpired = function(serializedRecording) {
|
|
|
2131
2131
|
};
|
|
2132
2132
|
|
|
2133
2133
|
// stateless utils
|
|
2134
|
+
// mostly from https://github.com/mixpanel/mixpanel-js/blob/989ada50f518edab47b9c4fd9535f9fbd5ec5fc0/src/autotrack-utils.js
|
|
2135
|
+
|
|
2134
2136
|
|
|
2135
2137
|
var EV_CHANGE = 'change';
|
|
2136
2138
|
var EV_CLICK = 'click';
|
|
@@ -2156,7 +2158,7 @@ var TRACKED_ATTRS = [
|
|
|
2156
2158
|
'href', 'name', 'role', 'title', 'type'
|
|
2157
2159
|
];
|
|
2158
2160
|
|
|
2159
|
-
var logger$
|
|
2161
|
+
var logger$4 = console_with_prefix('autocapture');
|
|
2160
2162
|
|
|
2161
2163
|
|
|
2162
2164
|
function getClasses(el) {
|
|
@@ -2242,6 +2244,7 @@ function getPropsForDOMEvent(ev, config) {
|
|
|
2242
2244
|
var blockSelectors = config.blockSelectors || [];
|
|
2243
2245
|
var captureTextContent = config.captureTextContent || false;
|
|
2244
2246
|
var captureExtraAttrs = config.captureExtraAttrs || [];
|
|
2247
|
+
var capturedForHeatMap = config.capturedForHeatMap || false;
|
|
2245
2248
|
|
|
2246
2249
|
// convert array to set every time, as the config may have changed
|
|
2247
2250
|
var blockAttrsSet = {};
|
|
@@ -2320,6 +2323,9 @@ function getPropsForDOMEvent(ev, config) {
|
|
|
2320
2323
|
props['$' + prop] = ev[prop];
|
|
2321
2324
|
}
|
|
2322
2325
|
});
|
|
2326
|
+
if (capturedForHeatMap) {
|
|
2327
|
+
props['$captured_for_heatmap'] = true;
|
|
2328
|
+
}
|
|
2323
2329
|
target = guessRealClickTarget(ev);
|
|
2324
2330
|
}
|
|
2325
2331
|
// prioritize text content from "real" click target if different from original target
|
|
@@ -2414,7 +2420,7 @@ function isElementAllowed(el, ev, allowElementCallback, allowSelectors) {
|
|
|
2414
2420
|
return false;
|
|
2415
2421
|
}
|
|
2416
2422
|
} catch (err) {
|
|
2417
|
-
logger$
|
|
2423
|
+
logger$4.critical('Error while checking element in allowElementCallback', err);
|
|
2418
2424
|
return false;
|
|
2419
2425
|
}
|
|
2420
2426
|
}
|
|
@@ -2431,7 +2437,7 @@ function isElementAllowed(el, ev, allowElementCallback, allowSelectors) {
|
|
|
2431
2437
|
return true;
|
|
2432
2438
|
}
|
|
2433
2439
|
} catch (err) {
|
|
2434
|
-
logger$
|
|
2440
|
+
logger$4.critical('Error while checking selector: ' + sel, err);
|
|
2435
2441
|
}
|
|
2436
2442
|
}
|
|
2437
2443
|
return false;
|
|
@@ -2446,7 +2452,7 @@ function isElementBlocked(el, ev, blockElementCallback, blockSelectors) {
|
|
|
2446
2452
|
return true;
|
|
2447
2453
|
}
|
|
2448
2454
|
} catch (err) {
|
|
2449
|
-
logger$
|
|
2455
|
+
logger$4.critical('Error while checking element in blockElementCallback', err);
|
|
2450
2456
|
return true;
|
|
2451
2457
|
}
|
|
2452
2458
|
}
|
|
@@ -2460,7 +2466,7 @@ function isElementBlocked(el, ev, blockElementCallback, blockSelectors) {
|
|
|
2460
2466
|
return true;
|
|
2461
2467
|
}
|
|
2462
2468
|
} catch (err) {
|
|
2463
|
-
logger$
|
|
2469
|
+
logger$4.critical('Error while checking selector: ' + sel, err);
|
|
2464
2470
|
}
|
|
2465
2471
|
}
|
|
2466
2472
|
}
|
|
@@ -2666,22 +2672,22 @@ var CONFIG_TRACK_PAGEVIEW = 'pageview';
|
|
|
2666
2672
|
var CONFIG_TRACK_SCROLL = 'scroll';
|
|
2667
2673
|
var CONFIG_TRACK_SUBMIT = 'submit';
|
|
2668
2674
|
|
|
2669
|
-
var CONFIG_DEFAULTS = {};
|
|
2670
|
-
CONFIG_DEFAULTS[CONFIG_ALLOW_SELECTORS] = [];
|
|
2671
|
-
CONFIG_DEFAULTS[CONFIG_ALLOW_URL_REGEXES] = [];
|
|
2672
|
-
CONFIG_DEFAULTS[CONFIG_BLOCK_ATTRS] = [];
|
|
2673
|
-
CONFIG_DEFAULTS[CONFIG_BLOCK_ELEMENT_CALLBACK] = null;
|
|
2674
|
-
CONFIG_DEFAULTS[CONFIG_BLOCK_SELECTORS] = [];
|
|
2675
|
-
CONFIG_DEFAULTS[CONFIG_BLOCK_URL_REGEXES] = [];
|
|
2676
|
-
CONFIG_DEFAULTS[CONFIG_CAPTURE_EXTRA_ATTRS] = [];
|
|
2677
|
-
CONFIG_DEFAULTS[CONFIG_CAPTURE_TEXT_CONTENT] = false;
|
|
2678
|
-
CONFIG_DEFAULTS[CONFIG_SCROLL_CAPTURE_ALL] = false;
|
|
2679
|
-
CONFIG_DEFAULTS[CONFIG_SCROLL_CHECKPOINTS] = [25, 50, 75, 100];
|
|
2680
|
-
CONFIG_DEFAULTS[CONFIG_TRACK_CLICK] = true;
|
|
2681
|
-
CONFIG_DEFAULTS[CONFIG_TRACK_INPUT] = true;
|
|
2682
|
-
CONFIG_DEFAULTS[CONFIG_TRACK_PAGEVIEW] = PAGEVIEW_OPTION_FULL_URL;
|
|
2683
|
-
CONFIG_DEFAULTS[CONFIG_TRACK_SCROLL] = true;
|
|
2684
|
-
CONFIG_DEFAULTS[CONFIG_TRACK_SUBMIT] = true;
|
|
2675
|
+
var CONFIG_DEFAULTS$1 = {};
|
|
2676
|
+
CONFIG_DEFAULTS$1[CONFIG_ALLOW_SELECTORS] = [];
|
|
2677
|
+
CONFIG_DEFAULTS$1[CONFIG_ALLOW_URL_REGEXES] = [];
|
|
2678
|
+
CONFIG_DEFAULTS$1[CONFIG_BLOCK_ATTRS] = [];
|
|
2679
|
+
CONFIG_DEFAULTS$1[CONFIG_BLOCK_ELEMENT_CALLBACK] = null;
|
|
2680
|
+
CONFIG_DEFAULTS$1[CONFIG_BLOCK_SELECTORS] = [];
|
|
2681
|
+
CONFIG_DEFAULTS$1[CONFIG_BLOCK_URL_REGEXES] = [];
|
|
2682
|
+
CONFIG_DEFAULTS$1[CONFIG_CAPTURE_EXTRA_ATTRS] = [];
|
|
2683
|
+
CONFIG_DEFAULTS$1[CONFIG_CAPTURE_TEXT_CONTENT] = false;
|
|
2684
|
+
CONFIG_DEFAULTS$1[CONFIG_SCROLL_CAPTURE_ALL] = false;
|
|
2685
|
+
CONFIG_DEFAULTS$1[CONFIG_SCROLL_CHECKPOINTS] = [25, 50, 75, 100];
|
|
2686
|
+
CONFIG_DEFAULTS$1[CONFIG_TRACK_CLICK] = true;
|
|
2687
|
+
CONFIG_DEFAULTS$1[CONFIG_TRACK_INPUT] = true;
|
|
2688
|
+
CONFIG_DEFAULTS$1[CONFIG_TRACK_PAGEVIEW] = PAGEVIEW_OPTION_FULL_URL;
|
|
2689
|
+
CONFIG_DEFAULTS$1[CONFIG_TRACK_SCROLL] = true;
|
|
2690
|
+
CONFIG_DEFAULTS$1[CONFIG_TRACK_SUBMIT] = true;
|
|
2685
2691
|
|
|
2686
2692
|
var DEFAULT_PROPS = {
|
|
2687
2693
|
'$mp_autocapture': true
|
|
@@ -2702,7 +2708,7 @@ var Autocapture = function(mp) {
|
|
|
2702
2708
|
|
|
2703
2709
|
Autocapture.prototype.init = function() {
|
|
2704
2710
|
if (!minDOMApisSupported()) {
|
|
2705
|
-
logger$
|
|
2711
|
+
logger$4.critical('Autocapture unavailable: missing required DOM APIs');
|
|
2706
2712
|
return;
|
|
2707
2713
|
}
|
|
2708
2714
|
|
|
@@ -2719,10 +2725,10 @@ Autocapture.prototype.getFullConfig = function() {
|
|
|
2719
2725
|
// Autocapture is completely off
|
|
2720
2726
|
return {};
|
|
2721
2727
|
} else if (_.isObject(autocaptureConfig)) {
|
|
2722
|
-
return _.extend({}, CONFIG_DEFAULTS, autocaptureConfig);
|
|
2728
|
+
return _.extend({}, CONFIG_DEFAULTS$1, autocaptureConfig);
|
|
2723
2729
|
} else {
|
|
2724
2730
|
// Autocapture config is non-object truthy value, return default
|
|
2725
|
-
return CONFIG_DEFAULTS;
|
|
2731
|
+
return CONFIG_DEFAULTS$1;
|
|
2726
2732
|
}
|
|
2727
2733
|
};
|
|
2728
2734
|
|
|
@@ -2746,7 +2752,7 @@ Autocapture.prototype.currentUrlBlocked = function() {
|
|
|
2746
2752
|
break;
|
|
2747
2753
|
}
|
|
2748
2754
|
} catch (err) {
|
|
2749
|
-
logger$
|
|
2755
|
+
logger$4.critical('Error while checking block URL regex: ' + allowRegex, err);
|
|
2750
2756
|
return true;
|
|
2751
2757
|
}
|
|
2752
2758
|
}
|
|
@@ -2767,7 +2773,7 @@ Autocapture.prototype.currentUrlBlocked = function() {
|
|
|
2767
2773
|
return true;
|
|
2768
2774
|
}
|
|
2769
2775
|
} catch (err) {
|
|
2770
|
-
logger$
|
|
2776
|
+
logger$4.critical('Error while checking block URL regex: ' + blockUrlRegexes[i], err);
|
|
2771
2777
|
return true;
|
|
2772
2778
|
}
|
|
2773
2779
|
}
|
|
@@ -2796,7 +2802,8 @@ Autocapture.prototype.trackDomEvent = function(ev, mpEventName) {
|
|
|
2796
2802
|
blockElementCallback: this.getConfig(CONFIG_BLOCK_ELEMENT_CALLBACK),
|
|
2797
2803
|
blockSelectors: this.getConfig(CONFIG_BLOCK_SELECTORS),
|
|
2798
2804
|
captureExtraAttrs: this.getConfig(CONFIG_CAPTURE_EXTRA_ATTRS),
|
|
2799
|
-
captureTextContent: this.getConfig(CONFIG_CAPTURE_TEXT_CONTENT)
|
|
2805
|
+
captureTextContent: this.getConfig(CONFIG_CAPTURE_TEXT_CONTENT),
|
|
2806
|
+
capturedForHeatMap: mpEventName === MP_EV_CLICK && !this.getConfig(CONFIG_TRACK_CLICK) && this.mp.is_recording_heatmap_data(),
|
|
2800
2807
|
});
|
|
2801
2808
|
if (props) {
|
|
2802
2809
|
_.extend(props, DEFAULT_PROPS);
|
|
@@ -2807,13 +2814,13 @@ Autocapture.prototype.trackDomEvent = function(ev, mpEventName) {
|
|
|
2807
2814
|
Autocapture.prototype.initClickTracking = function() {
|
|
2808
2815
|
win.removeEventListener(EV_CLICK, this.listenerClick);
|
|
2809
2816
|
|
|
2810
|
-
if (!this.getConfig(CONFIG_TRACK_CLICK)) {
|
|
2817
|
+
if (!this.getConfig(CONFIG_TRACK_CLICK) && !this.mp.get_config('record_heatmap_data')) {
|
|
2811
2818
|
return;
|
|
2812
2819
|
}
|
|
2813
|
-
logger$
|
|
2820
|
+
logger$4.log('Initializing click tracking');
|
|
2814
2821
|
|
|
2815
2822
|
this.listenerClick = win.addEventListener(EV_CLICK, function(ev) {
|
|
2816
|
-
if (!this.getConfig(CONFIG_TRACK_CLICK)) {
|
|
2823
|
+
if (!this.getConfig(CONFIG_TRACK_CLICK) && !this.mp.is_recording_heatmap_data()) {
|
|
2817
2824
|
return;
|
|
2818
2825
|
}
|
|
2819
2826
|
this.trackDomEvent(ev, MP_EV_CLICK);
|
|
@@ -2826,7 +2833,7 @@ Autocapture.prototype.initInputTracking = function() {
|
|
|
2826
2833
|
if (!this.getConfig(CONFIG_TRACK_INPUT)) {
|
|
2827
2834
|
return;
|
|
2828
2835
|
}
|
|
2829
|
-
logger$
|
|
2836
|
+
logger$4.log('Initializing input tracking');
|
|
2830
2837
|
|
|
2831
2838
|
this.listenerChange = win.addEventListener(EV_CHANGE, function(ev) {
|
|
2832
2839
|
if (!this.getConfig(CONFIG_TRACK_INPUT)) {
|
|
@@ -2844,7 +2851,7 @@ Autocapture.prototype.initPageviewTracking = function() {
|
|
|
2844
2851
|
if (!this.pageviewTrackingConfig()) {
|
|
2845
2852
|
return;
|
|
2846
2853
|
}
|
|
2847
|
-
logger$
|
|
2854
|
+
logger$4.log('Initializing pageview tracking');
|
|
2848
2855
|
|
|
2849
2856
|
var previousTrackedUrl = '';
|
|
2850
2857
|
var tracked = false;
|
|
@@ -2899,7 +2906,7 @@ Autocapture.prototype.initPageviewTracking = function() {
|
|
|
2899
2906
|
}
|
|
2900
2907
|
if (didPathChange) {
|
|
2901
2908
|
this.lastScrollCheckpoint = 0;
|
|
2902
|
-
logger$
|
|
2909
|
+
logger$4.log('Path change: re-initializing scroll depth checkpoints');
|
|
2903
2910
|
}
|
|
2904
2911
|
}
|
|
2905
2912
|
}.bind(this)));
|
|
@@ -2911,7 +2918,7 @@ Autocapture.prototype.initScrollTracking = function() {
|
|
|
2911
2918
|
if (!this.getConfig(CONFIG_TRACK_SCROLL)) {
|
|
2912
2919
|
return;
|
|
2913
2920
|
}
|
|
2914
|
-
logger$
|
|
2921
|
+
logger$4.log('Initializing scroll tracking');
|
|
2915
2922
|
this.lastScrollCheckpoint = 0;
|
|
2916
2923
|
|
|
2917
2924
|
this.listenerScroll = win.addEventListener(EV_SCROLLEND, safewrap(function() {
|
|
@@ -2948,7 +2955,7 @@ Autocapture.prototype.initScrollTracking = function() {
|
|
|
2948
2955
|
}
|
|
2949
2956
|
}
|
|
2950
2957
|
} catch (err) {
|
|
2951
|
-
logger$
|
|
2958
|
+
logger$4.critical('Error while calculating scroll percentage', err);
|
|
2952
2959
|
}
|
|
2953
2960
|
if (shouldTrack) {
|
|
2954
2961
|
this.mp.track(MP_EV_SCROLL, props);
|
|
@@ -2962,7 +2969,7 @@ Autocapture.prototype.initSubmitTracking = function() {
|
|
|
2962
2969
|
if (!this.getConfig(CONFIG_TRACK_SUBMIT)) {
|
|
2963
2970
|
return;
|
|
2964
2971
|
}
|
|
2965
|
-
logger$
|
|
2972
|
+
logger$4.log('Initializing submit tracking');
|
|
2966
2973
|
|
|
2967
2974
|
this.listenerSubmit = win.addEventListener(EV_SUBMIT, function(ev) {
|
|
2968
2975
|
if (!this.getConfig(CONFIG_TRACK_SUBMIT)) {
|
|
@@ -2975,8 +2982,196 @@ Autocapture.prototype.initSubmitTracking = function() {
|
|
|
2975
2982
|
// TODO integrate error_reporter from mixpanel instance
|
|
2976
2983
|
safewrapClass(Autocapture);
|
|
2977
2984
|
|
|
2985
|
+
var fetch = win['fetch'];
|
|
2986
|
+
var logger$3 = console_with_prefix('flags');
|
|
2987
|
+
|
|
2988
|
+
var FLAGS_CONFIG_KEY = 'flags';
|
|
2989
|
+
|
|
2990
|
+
var CONFIG_CONTEXT = 'context';
|
|
2991
|
+
var CONFIG_DEFAULTS = {};
|
|
2992
|
+
CONFIG_DEFAULTS[CONFIG_CONTEXT] = {};
|
|
2993
|
+
|
|
2994
|
+
/**
|
|
2995
|
+
* FeatureFlagManager: support for Mixpanel's feature flagging product
|
|
2996
|
+
* @constructor
|
|
2997
|
+
*/
|
|
2998
|
+
var FeatureFlagManager = function(initOptions) {
|
|
2999
|
+
this.getMpConfig = initOptions.getConfigFunc;
|
|
3000
|
+
this.getDistinctId = initOptions.getDistinctIdFunc;
|
|
3001
|
+
this.track = initOptions.trackingFunc;
|
|
3002
|
+
};
|
|
3003
|
+
|
|
3004
|
+
FeatureFlagManager.prototype.init = function() {
|
|
3005
|
+
if (!minApisSupported()) {
|
|
3006
|
+
logger$3.critical('Feature Flags unavailable: missing minimum required APIs');
|
|
3007
|
+
return;
|
|
3008
|
+
}
|
|
3009
|
+
|
|
3010
|
+
this.flags = null;
|
|
3011
|
+
this.fetchFlags();
|
|
3012
|
+
|
|
3013
|
+
this.trackedFeatures = new Set();
|
|
3014
|
+
};
|
|
3015
|
+
|
|
3016
|
+
FeatureFlagManager.prototype.getFullConfig = function() {
|
|
3017
|
+
var ffConfig = this.getMpConfig(FLAGS_CONFIG_KEY);
|
|
3018
|
+
if (!ffConfig) {
|
|
3019
|
+
// flags are completely off
|
|
3020
|
+
return {};
|
|
3021
|
+
} else if (_.isObject(ffConfig)) {
|
|
3022
|
+
return _.extend({}, CONFIG_DEFAULTS, ffConfig);
|
|
3023
|
+
} else {
|
|
3024
|
+
// config is non-object truthy value, return default
|
|
3025
|
+
return CONFIG_DEFAULTS;
|
|
3026
|
+
}
|
|
3027
|
+
};
|
|
3028
|
+
|
|
3029
|
+
FeatureFlagManager.prototype.getConfig = function(key) {
|
|
3030
|
+
return this.getFullConfig()[key];
|
|
3031
|
+
};
|
|
3032
|
+
|
|
3033
|
+
FeatureFlagManager.prototype.isEnabled = function() {
|
|
3034
|
+
return !!this.getMpConfig(FLAGS_CONFIG_KEY);
|
|
3035
|
+
};
|
|
3036
|
+
|
|
3037
|
+
FeatureFlagManager.prototype.areFeaturesReady = function() {
|
|
3038
|
+
if (!this.isEnabled()) {
|
|
3039
|
+
logger$3.error('Feature Flags not enabled');
|
|
3040
|
+
}
|
|
3041
|
+
return !!this.flags;
|
|
3042
|
+
};
|
|
3043
|
+
|
|
3044
|
+
FeatureFlagManager.prototype.fetchFlags = function() {
|
|
3045
|
+
if (!this.isEnabled()) {
|
|
3046
|
+
return;
|
|
3047
|
+
}
|
|
3048
|
+
|
|
3049
|
+
var distinctId = this.getDistinctId();
|
|
3050
|
+
logger$3.log('Fetching flags for distinct ID: ' + distinctId);
|
|
3051
|
+
var reqParams = {
|
|
3052
|
+
'context': _.extend({'distinct_id': distinctId}, this.getConfig(CONFIG_CONTEXT))
|
|
3053
|
+
};
|
|
3054
|
+
this.fetchPromise = win['fetch'](this.getMpConfig('api_host') + '/' + this.getMpConfig('api_routes')['flags'], {
|
|
3055
|
+
'method': 'POST',
|
|
3056
|
+
'headers': {
|
|
3057
|
+
'Authorization': 'Basic ' + btoa(this.getMpConfig('token') + ':'),
|
|
3058
|
+
'Content-Type': 'application/octet-stream'
|
|
3059
|
+
},
|
|
3060
|
+
'body': JSON.stringify(reqParams)
|
|
3061
|
+
}).then(function(response) {
|
|
3062
|
+
return response.json().then(function(responseBody) {
|
|
3063
|
+
var responseFlags = responseBody['flags'];
|
|
3064
|
+
if (!responseFlags) {
|
|
3065
|
+
throw new Error('No flags in API response');
|
|
3066
|
+
}
|
|
3067
|
+
var flags = new Map();
|
|
3068
|
+
_.each(responseFlags, function(data, key) {
|
|
3069
|
+
flags.set(key, {
|
|
3070
|
+
'key': data['variant_key'],
|
|
3071
|
+
'data': data['variant_value']
|
|
3072
|
+
});
|
|
3073
|
+
});
|
|
3074
|
+
this.flags = flags;
|
|
3075
|
+
}.bind(this)).catch(function(error) {
|
|
3076
|
+
logger$3.error(error);
|
|
3077
|
+
});
|
|
3078
|
+
}.bind(this)).catch(function() {});
|
|
3079
|
+
};
|
|
3080
|
+
|
|
3081
|
+
FeatureFlagManager.prototype.getFeature = function(featureName, fallback) {
|
|
3082
|
+
if (!this.fetchPromise) {
|
|
3083
|
+
return new Promise(function(resolve) {
|
|
3084
|
+
logger$3.critical('Feature Flags not initialized');
|
|
3085
|
+
resolve(fallback);
|
|
3086
|
+
});
|
|
3087
|
+
}
|
|
3088
|
+
|
|
3089
|
+
return this.fetchPromise.then(function() {
|
|
3090
|
+
return this.getFeatureSync(featureName, fallback);
|
|
3091
|
+
}.bind(this)).catch(function(error) {
|
|
3092
|
+
logger$3.error(error);
|
|
3093
|
+
return fallback;
|
|
3094
|
+
});
|
|
3095
|
+
};
|
|
3096
|
+
|
|
3097
|
+
FeatureFlagManager.prototype.getFeatureSync = function(featureName, fallback) {
|
|
3098
|
+
if (!this.areFeaturesReady()) {
|
|
3099
|
+
logger$3.log('Flags not loaded yet');
|
|
3100
|
+
return fallback;
|
|
3101
|
+
}
|
|
3102
|
+
var feature = this.flags.get(featureName);
|
|
3103
|
+
if (!feature) {
|
|
3104
|
+
logger$3.log('No flag found: "' + featureName + '"');
|
|
3105
|
+
return fallback;
|
|
3106
|
+
}
|
|
3107
|
+
this.trackFeatureCheck(featureName, feature);
|
|
3108
|
+
return feature;
|
|
3109
|
+
};
|
|
3110
|
+
|
|
3111
|
+
FeatureFlagManager.prototype.getFeatureData = function(featureName, fallbackValue) {
|
|
3112
|
+
return this.getFeature(featureName, {'data': fallbackValue}).then(function(feature) {
|
|
3113
|
+
return feature['data'];
|
|
3114
|
+
}).catch(function(error) {
|
|
3115
|
+
logger$3.error(error);
|
|
3116
|
+
return fallbackValue;
|
|
3117
|
+
});
|
|
3118
|
+
};
|
|
3119
|
+
|
|
3120
|
+
FeatureFlagManager.prototype.getFeatureDataSync = function(featureName, fallbackValue) {
|
|
3121
|
+
return this.getFeatureSync(featureName, {'data': fallbackValue})['data'];
|
|
3122
|
+
};
|
|
3123
|
+
|
|
3124
|
+
FeatureFlagManager.prototype.isFeatureEnabled = function(featureName, fallbackValue) {
|
|
3125
|
+
return this.getFeatureData(featureName).then(function() {
|
|
3126
|
+
return this.isFeatureEnabledSync(featureName, fallbackValue);
|
|
3127
|
+
}.bind(this)).catch(function(error) {
|
|
3128
|
+
logger$3.error(error);
|
|
3129
|
+
return fallbackValue;
|
|
3130
|
+
});
|
|
3131
|
+
};
|
|
3132
|
+
|
|
3133
|
+
FeatureFlagManager.prototype.isFeatureEnabledSync = function(featureName, fallbackValue) {
|
|
3134
|
+
fallbackValue = fallbackValue || false;
|
|
3135
|
+
var val = this.getFeatureDataSync(featureName, fallbackValue);
|
|
3136
|
+
if (val !== true && val !== false) {
|
|
3137
|
+
logger$3.error('Feature flag "' + featureName + '" value: ' + val + ' is not a boolean; returning fallback value: ' + fallbackValue);
|
|
3138
|
+
val = fallbackValue;
|
|
3139
|
+
}
|
|
3140
|
+
return val;
|
|
3141
|
+
};
|
|
3142
|
+
|
|
3143
|
+
FeatureFlagManager.prototype.trackFeatureCheck = function(featureName, feature) {
|
|
3144
|
+
if (this.trackedFeatures.has(featureName)) {
|
|
3145
|
+
return;
|
|
3146
|
+
}
|
|
3147
|
+
this.trackedFeatures.add(featureName);
|
|
3148
|
+
this.track('$experiment_started', {
|
|
3149
|
+
'Experiment name': featureName,
|
|
3150
|
+
'Variant name': feature['key'],
|
|
3151
|
+
'$experiment_type': 'feature_flag'
|
|
3152
|
+
});
|
|
3153
|
+
};
|
|
3154
|
+
|
|
3155
|
+
function minApisSupported() {
|
|
3156
|
+
return !!fetch &&
|
|
3157
|
+
typeof Promise !== 'undefined' &&
|
|
3158
|
+
typeof Map !== 'undefined' &&
|
|
3159
|
+
typeof Set !== 'undefined';
|
|
3160
|
+
}
|
|
3161
|
+
|
|
3162
|
+
safewrapClass(FeatureFlagManager);
|
|
3163
|
+
|
|
3164
|
+
FeatureFlagManager.prototype['are_features_ready'] = FeatureFlagManager.prototype.areFeaturesReady;
|
|
3165
|
+
FeatureFlagManager.prototype['get_feature'] = FeatureFlagManager.prototype.getFeature;
|
|
3166
|
+
FeatureFlagManager.prototype['get_feature_data'] = FeatureFlagManager.prototype.getFeatureData;
|
|
3167
|
+
FeatureFlagManager.prototype['get_feature_data_sync'] = FeatureFlagManager.prototype.getFeatureDataSync;
|
|
3168
|
+
FeatureFlagManager.prototype['get_feature_sync'] = FeatureFlagManager.prototype.getFeatureSync;
|
|
3169
|
+
FeatureFlagManager.prototype['is_feature_enabled'] = FeatureFlagManager.prototype.isFeatureEnabled;
|
|
3170
|
+
FeatureFlagManager.prototype['is_feature_enabled_sync'] = FeatureFlagManager.prototype.isFeatureEnabledSync;
|
|
3171
|
+
|
|
2978
3172
|
/* eslint camelcase: "off" */
|
|
2979
3173
|
|
|
3174
|
+
|
|
2980
3175
|
/**
|
|
2981
3176
|
* DomTracker Object
|
|
2982
3177
|
* @constructor
|
|
@@ -3280,7 +3475,7 @@ SharedLock.prototype.withLock = function(lockedCB, pid) {
|
|
|
3280
3475
|
* @type {import('./wrapper').StorageWrapper}
|
|
3281
3476
|
*/
|
|
3282
3477
|
var LocalStorageWrapper = function (storageOverride) {
|
|
3283
|
-
this.storage = storageOverride || localStorage;
|
|
3478
|
+
this.storage = storageOverride || win.localStorage;
|
|
3284
3479
|
};
|
|
3285
3480
|
|
|
3286
3481
|
LocalStorageWrapper.prototype.init = function () {
|
|
@@ -4027,6 +4222,7 @@ RequestBatcher.prototype.reportError = function(msg, err) {
|
|
|
4027
4222
|
* These functions are used internally by the SDK and are not intended to be publicly exposed.
|
|
4028
4223
|
*/
|
|
4029
4224
|
|
|
4225
|
+
|
|
4030
4226
|
/**
|
|
4031
4227
|
* A function used to track a Mixpanel event (e.g. MixpanelLib.track)
|
|
4032
4228
|
* @callback trackFunction
|
|
@@ -4315,6 +4511,7 @@ function _addOptOutCheck(method, getConfigValue) {
|
|
|
4315
4511
|
|
|
4316
4512
|
/* eslint camelcase: "off" */
|
|
4317
4513
|
|
|
4514
|
+
|
|
4318
4515
|
/** @const */ var SET_ACTION = '$set';
|
|
4319
4516
|
/** @const */ var SET_ONCE_ACTION = '$set_once';
|
|
4320
4517
|
/** @const */ var UNSET_ACTION = '$unset';
|
|
@@ -5074,6 +5271,7 @@ MixpanelPeople.prototype['toString'] = MixpanelPeople.prototype.toString;
|
|
|
5074
5271
|
|
|
5075
5272
|
/* eslint camelcase: "off" */
|
|
5076
5273
|
|
|
5274
|
+
|
|
5077
5275
|
/*
|
|
5078
5276
|
* Constants
|
|
5079
5277
|
*/
|
|
@@ -5686,10 +5884,11 @@ if (navigator['sendBeacon']) {
|
|
|
5686
5884
|
}
|
|
5687
5885
|
|
|
5688
5886
|
var DEFAULT_API_ROUTES = {
|
|
5689
|
-
'track':
|
|
5887
|
+
'track': 'track/',
|
|
5690
5888
|
'engage': 'engage/',
|
|
5691
5889
|
'groups': 'groups/',
|
|
5692
|
-
'record': 'record/'
|
|
5890
|
+
'record': 'record/',
|
|
5891
|
+
'flags': 'flags/'
|
|
5693
5892
|
};
|
|
5694
5893
|
|
|
5695
5894
|
/*
|
|
@@ -5707,6 +5906,7 @@ var DEFAULT_CONFIG = {
|
|
|
5707
5906
|
'cross_site_cookie': false,
|
|
5708
5907
|
'cross_subdomain_cookie': true,
|
|
5709
5908
|
'error_reporter': NOOP_FUNC,
|
|
5909
|
+
'flags': false,
|
|
5710
5910
|
'persistence': 'cookie',
|
|
5711
5911
|
'persistence_name': '',
|
|
5712
5912
|
'cookie_domain': '',
|
|
@@ -5747,6 +5947,7 @@ var DEFAULT_CONFIG = {
|
|
|
5747
5947
|
'record_block_selector': 'img, video',
|
|
5748
5948
|
'record_canvas': false,
|
|
5749
5949
|
'record_collect_fonts': false,
|
|
5950
|
+
'record_heatmap_data': false,
|
|
5750
5951
|
'record_idle_timeout_ms': 30 * 60 * 1000, // 30 minutes
|
|
5751
5952
|
'record_mask_text_class': new RegExp('^(mp-mask|fs-mask|amp-mask|rr-mask|ph-mask)$'),
|
|
5752
5953
|
'record_mask_text_selector': '*',
|
|
@@ -5962,6 +6163,14 @@ MixpanelLib.prototype._init = function(token, config, name) {
|
|
|
5962
6163
|
}, '');
|
|
5963
6164
|
}
|
|
5964
6165
|
|
|
6166
|
+
this.flags = new FeatureFlagManager({
|
|
6167
|
+
getConfigFunc: _.bind(this.get_config, this),
|
|
6168
|
+
getDistinctIdFunc: _.bind(this.get_distinct_id, this),
|
|
6169
|
+
trackingFunc: _.bind(this.track, this)
|
|
6170
|
+
});
|
|
6171
|
+
this.flags.init();
|
|
6172
|
+
this['flags'] = this.flags;
|
|
6173
|
+
|
|
5965
6174
|
this.autocapture = new Autocapture(this);
|
|
5966
6175
|
this.autocapture.init();
|
|
5967
6176
|
|
|
@@ -6087,6 +6296,10 @@ MixpanelLib.prototype.resume_session_recording = function () {
|
|
|
6087
6296
|
}
|
|
6088
6297
|
};
|
|
6089
6298
|
|
|
6299
|
+
MixpanelLib.prototype.is_recording_heatmap_data = function () {
|
|
6300
|
+
return this._get_session_replay_id() && this.get_config('record_heatmap_data');
|
|
6301
|
+
};
|
|
6302
|
+
|
|
6090
6303
|
MixpanelLib.prototype.get_session_recording_properties = function () {
|
|
6091
6304
|
var props = {};
|
|
6092
6305
|
var replay_id = this._get_session_replay_id();
|
|
@@ -7168,6 +7381,11 @@ MixpanelLib.prototype.identify = function(
|
|
|
7168
7381
|
'$anon_distinct_id': previous_distinct_id
|
|
7169
7382
|
}, {skip_hooks: true});
|
|
7170
7383
|
}
|
|
7384
|
+
|
|
7385
|
+
// check feature flags again if distinct id has changed
|
|
7386
|
+
if (new_distinct_id !== previous_distinct_id) {
|
|
7387
|
+
this.flags.fetchFlags();
|
|
7388
|
+
}
|
|
7171
7389
|
};
|
|
7172
7390
|
|
|
7173
7391
|
/**
|
|
@@ -7442,7 +7660,7 @@ MixpanelLib.prototype.set_config = function(config) {
|
|
|
7442
7660
|
}
|
|
7443
7661
|
Config.DEBUG = Config.DEBUG || this.get_config('debug');
|
|
7444
7662
|
|
|
7445
|
-
if ('autocapture' in config && this.autocapture) {
|
|
7663
|
+
if (('autocapture' in config || 'record_heatmap_data' in config) && this.autocapture) {
|
|
7446
7664
|
this.autocapture.init();
|
|
7447
7665
|
}
|
|
7448
7666
|
}
|
|
@@ -7936,6 +8154,7 @@ function init_as_module(bundle_loader) {
|
|
|
7936
8154
|
}
|
|
7937
8155
|
|
|
7938
8156
|
// For loading separate bundles asynchronously via script tag
|
|
8157
|
+
// so that we don't load them until they are needed at runtime.
|
|
7939
8158
|
|
|
7940
8159
|
// For builds that do NOT want any extra bundles (e.g. session recorder)
|
|
7941
8160
|
// and just the main SDK, throw an error when trying to load a separate bundle.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
(function(d,
|
|
2
|
-
for(
|
|
3
|
-
|
|
4
|
-
(function(d,
|
|
5
|
-
d[
|
|
1
|
+
(function(d,e){if(!e.__SV){var m,c;window.mixpanel=e;e._i=[];e.init=function(b,f,l){function t(k,g){var n=g.split(".");2==n.length&&(k=k[n[0]],g=n[1]);k[g]=function(){k.push([g].concat(Array.prototype.slice.call(arguments,0)))}}var h=e;"undefined"!==typeof l?h=e[l]=[]:l="mixpanel";h.people=h.people||[];h.toString=function(k){var g="mixpanel";"mixpanel"!==l&&(g+="."+l);k||(g+=" (stub)");return g};h.people.toString=function(){return h.toString(1)+".people (stub)"};m="disable time_event track track_pageview track_links track_forms track_with_groups add_group set_group remove_group register register_once alias unregister identify name_tag set_config reset opt_in_tracking opt_out_tracking has_opted_in_tracking has_opted_out_tracking clear_opt_in_out_tracking start_batch_senders start_session_recording stop_session_recording people.set people.set_once people.unset people.increment people.append people.union people.track_charge people.clear_charges people.delete_user people.remove".split(" ");
|
|
2
|
+
for(c=0;c<m.length;c++)t(h,m[c]);var q="set set_once union unset remove delete".split(" ");h.get_group=function(){function k(r){g[r]=function(){h.push([n,[r].concat(Array.prototype.slice.call(arguments,0))])}}for(var g={},n=["get_group"].concat(Array.prototype.slice.call(arguments,0)),p=0;p<q.length;p++)k(q[p]);return g};e._i.push([b,f,l])};e.__SV=1.2;var a=d.createElement("script");a.type="text/javascript";a.async=!0;a.src="undefined"!==typeof MIXPANEL_CUSTOM_LIB_URL?MIXPANEL_CUSTOM_LIB_URL:"file:"===
|
|
3
|
+
d.location.protocol&&"//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js".match(/^\/\//)?"https://cdn.mxpnl.com/libs/mixpanel-2-latest.min.js":"//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js";d=d.getElementsByTagName("script")[0];d.parentNode.insertBefore(a,d)}})(document,window.mixpanel||[]);
|
|
4
|
+
(function(d,e){if(d.mixpanel&&"function"===typeof d.mixpanel.init){var m="add_group alias clear_opt_in_out_tracking disable identify opt_in_tracking opt_out_tracking register register_once remove_group reset set_config set_group start_session_recording stop_session_recording time_event track track_forms track_links track_pageview track_with_groups unregister people.append people.clear_charges people.delete_user people.increment people.remove people.set people.set_once people.track_charge people.union people.unset group.remove group.set group.set_once group.union group.unset".split(" ");d[e]=
|
|
5
|
+
d[e]||function(){var c=[].slice.call(arguments,0),a=c.shift(),b=null,f=a.match(/^([^.]+)\.(.+)$/);f&&3===f.length&&!/people|group/.test(f[1])&&(b=f[1],a=f[2]);if((b=b?window.mixpanel[b]:window.mixpanel)&&-1!==m.indexOf(a))return/^people\./.test(a)?(a=a.split(".").pop(),b.people[a].apply(b.people,c),c=void 0):/^group\./.test(a)?(a=a.split(".").pop(),f=c.shift(),Array.isArray(f)&&2===f.length&&(b=b.get_group.apply(b,f),b[a].apply(b,c)),c=void 0):c=b.push.apply(b,[[a].concat(c)]),c}}})(window,"_mixpanel")
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
(function(
|
|
2
|
-
for(h=0;h<
|
|
3
|
-
|
|
1
|
+
(function(e,c){if(!c.__SV){var l,h;window.mixpanel=c;c._i=[];c.init=function(q,r,f){function t(d,a){var g=a.split(".");2==g.length&&(d=d[g[0]],a=g[1]);d[a]=function(){d.push([a].concat(Array.prototype.slice.call(arguments,0)))}}var b=c;"undefined"!==typeof f?b=c[f]=[]:f="mixpanel";b.people=b.people||[];b.toString=function(d){var a="mixpanel";"mixpanel"!==f&&(a+="."+f);d||(a+=" (stub)");return a};b.people.toString=function(){return b.toString(1)+".people (stub)"};l="disable time_event track track_pageview track_links track_forms track_with_groups add_group set_group remove_group register register_once alias unregister identify name_tag set_config reset opt_in_tracking opt_out_tracking has_opted_in_tracking has_opted_out_tracking clear_opt_in_out_tracking start_batch_senders start_session_recording stop_session_recording people.set people.set_once people.unset people.increment people.append people.union people.track_charge people.clear_charges people.delete_user people.remove".split(" ");
|
|
2
|
+
for(h=0;h<l.length;h++)t(b,l[h]);var n="set set_once union unset remove delete".split(" ");b.get_group=function(){function d(p){a[p]=function(){b.push([g,[p].concat(Array.prototype.slice.call(arguments,0))])}}for(var a={},g=["get_group"].concat(Array.prototype.slice.call(arguments,0)),m=0;m<n.length;m++)d(n[m]);return a};c._i.push([q,r,f])};c.__SV=1.2;var k=e.createElement("script");k.type="text/javascript";k.async=!0;k.src="undefined"!==typeof MIXPANEL_CUSTOM_LIB_URL?MIXPANEL_CUSTOM_LIB_URL:"file:"===
|
|
3
|
+
e.location.protocol&&"//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js".match(/^\/\//)?"https://cdn.mxpnl.com/libs/mixpanel-2-latest.min.js":"//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js";e=e.getElementsByTagName("script")[0];e.parentNode.insertBefore(k,e)}})(document,window.mixpanel||[])
|