mixpanel-browser 2.74.0 → 2.76.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/.claude/settings.local.json +3 -1
- package/.github/workflows/integration-tests.yml +2 -2
- package/.github/workflows/unit-tests.yml +3 -3
- package/CHANGELOG.md +15 -0
- package/README.md +2 -2
- package/build.sh +10 -8
- package/dist/async-modules/mixpanel-recorder-bIS4LMGd.js +23595 -0
- package/dist/async-modules/mixpanel-recorder-hFoTniVR.min.js +2 -0
- package/dist/async-modules/mixpanel-recorder-hFoTniVR.min.js.map +1 -0
- package/dist/async-modules/mixpanel-targeting-BcAPS-Mz.js +2520 -0
- package/dist/async-modules/mixpanel-targeting-VOeN7RWY.min.js +2 -0
- package/dist/async-modules/mixpanel-targeting-VOeN7RWY.min.js.map +1 -0
- package/dist/mixpanel-core.cjs.d.ts +68 -0
- package/dist/mixpanel-core.cjs.js +802 -337
- package/dist/mixpanel-recorder.js +828 -40
- package/dist/mixpanel-recorder.min.js +1 -1
- package/dist/mixpanel-recorder.min.js.map +1 -1
- package/dist/mixpanel-targeting.js +2520 -0
- package/dist/mixpanel-targeting.min.js +2 -0
- package/dist/mixpanel-targeting.min.js.map +1 -0
- package/dist/mixpanel-with-async-modules.cjs.d.ts +590 -0
- package/dist/mixpanel-with-async-modules.cjs.js +9867 -0
- package/dist/mixpanel-with-async-recorder.cjs.d.ts +68 -0
- package/dist/mixpanel-with-async-recorder.cjs.js +802 -337
- package/dist/mixpanel-with-recorder.d.ts +68 -0
- package/dist/mixpanel-with-recorder.js +1591 -343
- package/dist/mixpanel-with-recorder.min.d.ts +68 -0
- package/dist/mixpanel-with-recorder.min.js +1 -1
- package/dist/mixpanel.amd.d.ts +68 -0
- package/dist/mixpanel.amd.js +2124 -345
- package/dist/mixpanel.cjs.d.ts +68 -0
- package/dist/mixpanel.cjs.js +2124 -345
- package/dist/mixpanel.globals.js +802 -337
- package/dist/mixpanel.min.js +185 -175
- package/dist/mixpanel.module.d.ts +68 -0
- package/dist/mixpanel.module.js +2124 -345
- package/dist/mixpanel.umd.d.ts +68 -0
- package/dist/mixpanel.umd.js +2124 -345
- package/dist/rrweb-bundled.js +119 -5
- package/dist/rrweb-compiled.js +116 -5
- package/logo.svg +5 -0
- package/package.json +5 -3
- package/rollup.config.mjs +189 -40
- package/src/autocapture/index.js +10 -27
- package/src/config.js +9 -3
- package/src/flags/index.js +269 -9
- package/src/index.d.ts +68 -0
- package/src/loaders/loader-module.js +1 -0
- package/src/mixpanel-core.js +83 -109
- package/src/recorder/index.js +2 -1
- package/src/recorder/recorder.js +5 -1
- package/src/recorder/rrweb-network-plugin.js +649 -0
- package/src/recorder/session-recording.js +31 -11
- package/src/recorder-manager.js +216 -0
- package/src/request-batcher.js +1 -1
- package/src/targeting/event-matcher.js +42 -0
- package/src/targeting/index.js +11 -0
- package/src/targeting/loader.js +36 -0
- package/src/utils.js +14 -9
- package/testServer.js +55 -0
- /package/src/loaders/{loader-module-with-async-recorder.js → loader-module-with-async-modules.js} +0 -0
package/src/mixpanel-core.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/* eslint camelcase: "off" */
|
|
2
|
-
import Config from './config';
|
|
2
|
+
import {Config, TARGETING_FILENAME} from './config';
|
|
3
3
|
import { MAX_RECORDING_MS, _, console, userAgent, document, navigator, slice, NOOP_FUNC, JSONStringify } from './utils';
|
|
4
|
-
import { isRecordingExpired } from './recorder/utils';
|
|
5
4
|
import { window } from './window';
|
|
6
5
|
import { Autocapture } from './autocapture';
|
|
7
6
|
import { FeatureFlagManager } from './flags';
|
|
7
|
+
import { RecorderManager } from './recorder-manager';
|
|
8
8
|
import { FormTracker, LinkTracker } from './dom-trackers';
|
|
9
9
|
import { RequestBatcher } from './request-batcher';
|
|
10
10
|
import { MixpanelGroup } from './mixpanel-group';
|
|
@@ -22,7 +22,6 @@ import {
|
|
|
22
22
|
clearOptInOut,
|
|
23
23
|
addOptOutCheckMixpanelLib
|
|
24
24
|
} from './gdpr-utils';
|
|
25
|
-
import { IDBStorageWrapper, RECORDING_REGISTRY_STORE_NAME } from './storage/indexed-db';
|
|
26
25
|
|
|
27
26
|
/*
|
|
28
27
|
* Mixpanel JS Library
|
|
@@ -156,12 +155,17 @@ var DEFAULT_CONFIG = {
|
|
|
156
155
|
'record_collect_fonts': false,
|
|
157
156
|
'record_console': true,
|
|
158
157
|
'record_heatmap_data': false,
|
|
158
|
+
'recording_event_triggers': {},
|
|
159
159
|
'record_idle_timeout_ms': 30 * 60 * 1000, // 30 minutes
|
|
160
160
|
'record_mask_inputs': true,
|
|
161
161
|
'record_max_ms': MAX_RECORDING_MS,
|
|
162
162
|
'record_min_ms': 0,
|
|
163
|
+
'record_network': false,
|
|
164
|
+
'record_network_options': {},
|
|
163
165
|
'record_sessions_percent': 0,
|
|
164
|
-
'recorder_src':
|
|
166
|
+
'recorder_src': null,
|
|
167
|
+
'targeting_src': null,
|
|
168
|
+
'lib_base_path': 'https://cdn.mxpnl.com/libs/',
|
|
165
169
|
'remote_settings_mode': SETTING_DISABLED // 'strict', 'fallback', 'disabled'
|
|
166
170
|
};
|
|
167
171
|
|
|
@@ -315,6 +319,19 @@ MixpanelLib.prototype._init = function(token, config, name) {
|
|
|
315
319
|
'callback_fn': ((name === PRIMARY_INSTANCE_NAME) ? name : PRIMARY_INSTANCE_NAME + '.' + name) + '._jsc'
|
|
316
320
|
}));
|
|
317
321
|
|
|
322
|
+
this.recorderManager = new RecorderManager({
|
|
323
|
+
mixpanelInstance: this,
|
|
324
|
+
getConfigFunc: _.bind(this.get_config, this),
|
|
325
|
+
setConfigFunc: _.bind(this.set_config, this),
|
|
326
|
+
getTabIdFunc: _.bind(this.get_tab_id, this),
|
|
327
|
+
reportErrorFunc: _.bind(this.report_error, this),
|
|
328
|
+
getDistinctIdFunc: _.bind(this.get_distinct_id, this),
|
|
329
|
+
recorderSrc: this.get_config('recorder_src'),
|
|
330
|
+
targetingSrc: this.get_config('targeting_src'),
|
|
331
|
+
libBasePath: this.get_config('lib_base_path'),
|
|
332
|
+
loadExtraBundle: load_extra_bundle
|
|
333
|
+
});
|
|
334
|
+
|
|
318
335
|
this['_jsc'] = NOOP_FUNC;
|
|
319
336
|
|
|
320
337
|
this.__dom_loaded_queue = [];
|
|
@@ -391,7 +408,9 @@ MixpanelLib.prototype._init = function(token, config, name) {
|
|
|
391
408
|
getConfigFunc: _.bind(this.get_config, this),
|
|
392
409
|
setConfigFunc: _.bind(this.set_config, this),
|
|
393
410
|
getPropertyFunc: _.bind(this.get_property, this),
|
|
394
|
-
trackingFunc: _.bind(this.track, this)
|
|
411
|
+
trackingFunc: _.bind(this.track, this),
|
|
412
|
+
loadExtraBundle: load_extra_bundle,
|
|
413
|
+
targetingSrc: this.get_config('targeting_src') || (this.get_config('lib_base_path') + TARGETING_FILENAME)
|
|
395
414
|
});
|
|
396
415
|
this.flags.init();
|
|
397
416
|
this['flags'] = this.flags;
|
|
@@ -404,11 +423,11 @@ MixpanelLib.prototype._init = function(token, config, name) {
|
|
|
404
423
|
// Based on remote_settings_mode, fetch remote settings and then start session recording if applicable
|
|
405
424
|
var mode = this.get_config('remote_settings_mode');
|
|
406
425
|
if (mode === SETTING_STRICT || mode === SETTING_FALLBACK) {
|
|
407
|
-
this._fetch_remote_settings(mode).then(_.bind(function() {
|
|
408
|
-
this._check_and_start_session_recording();
|
|
426
|
+
this.__session_recording_init_promise = this._fetch_remote_settings(mode).then(_.bind(function() {
|
|
427
|
+
return this._check_and_start_session_recording();
|
|
409
428
|
}, this));
|
|
410
429
|
} else {
|
|
411
|
-
this._check_and_start_session_recording();
|
|
430
|
+
this.__session_recording_init_promise = this._check_and_start_session_recording();
|
|
412
431
|
}
|
|
413
432
|
};
|
|
414
433
|
|
|
@@ -452,132 +471,50 @@ MixpanelLib.prototype.get_tab_id = function () {
|
|
|
452
471
|
return this.tab_id || null;
|
|
453
472
|
};
|
|
454
473
|
|
|
455
|
-
MixpanelLib.prototype._should_load_recorder = function () {
|
|
456
|
-
if (this.get_config('disable_persistence')) {
|
|
457
|
-
console.log('Load recorder check skipped due to disable_persistence config');
|
|
458
|
-
return Promise.resolve(false);
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
var recording_registry_idb = new IDBStorageWrapper(RECORDING_REGISTRY_STORE_NAME);
|
|
462
|
-
var tab_id = this.get_tab_id();
|
|
463
|
-
return recording_registry_idb.init()
|
|
464
|
-
.then(function () {
|
|
465
|
-
return recording_registry_idb.getAll();
|
|
466
|
-
})
|
|
467
|
-
.then(function (recordings) {
|
|
468
|
-
for (var i = 0; i < recordings.length; i++) {
|
|
469
|
-
// if there are expired recordings in the registry, we should load the recorder to flush them
|
|
470
|
-
// if there's a recording for this tab id, we should load the recorder to continue the recording
|
|
471
|
-
if (isRecordingExpired(recordings[i]) || recordings[i]['tabId'] === tab_id) {
|
|
472
|
-
return true;
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
return false;
|
|
476
|
-
})
|
|
477
|
-
.catch(_.bind(function (err) {
|
|
478
|
-
this.report_error('Error checking recording registry', err);
|
|
479
|
-
}, this));
|
|
480
|
-
};
|
|
481
|
-
|
|
482
474
|
MixpanelLib.prototype._check_and_start_session_recording = addOptOutCheckMixpanelLib(function(force_start) {
|
|
483
|
-
|
|
484
|
-
console.critical('Browser does not support MutationObserver; skipping session recording');
|
|
485
|
-
return;
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
var loadRecorder = _.bind(function(startNewIfInactive) {
|
|
489
|
-
var handleLoadedRecorder = _.bind(function() {
|
|
490
|
-
this._recorder = this._recorder || new window['__mp_recorder'](this);
|
|
491
|
-
this._recorder['resumeRecording'](startNewIfInactive);
|
|
492
|
-
}, this);
|
|
493
|
-
|
|
494
|
-
if (_.isUndefined(window['__mp_recorder'])) {
|
|
495
|
-
load_extra_bundle(this.get_config('recorder_src'), handleLoadedRecorder);
|
|
496
|
-
} else {
|
|
497
|
-
handleLoadedRecorder();
|
|
498
|
-
}
|
|
499
|
-
}, this);
|
|
500
|
-
|
|
501
|
-
/**
|
|
502
|
-
* If the user is sampled or start_session_recording is called, we always load the recorder since it's guaranteed a recording should start.
|
|
503
|
-
* Otherwise, if the recording registry has any records then it's likely there's a recording in progress or orphaned data that needs to be flushed.
|
|
504
|
-
*/
|
|
505
|
-
var is_sampled = this.get_config('record_sessions_percent') > 0 && Math.random() * 100 <= this.get_config('record_sessions_percent');
|
|
506
|
-
if (force_start || is_sampled) {
|
|
507
|
-
loadRecorder(true);
|
|
508
|
-
} else {
|
|
509
|
-
this._should_load_recorder()
|
|
510
|
-
.then(function (shouldLoad) {
|
|
511
|
-
if (shouldLoad) {
|
|
512
|
-
loadRecorder(false);
|
|
513
|
-
}
|
|
514
|
-
});
|
|
515
|
-
}
|
|
475
|
+
return this.recorderManager.checkAndStartSessionRecording(force_start);
|
|
516
476
|
});
|
|
517
477
|
|
|
478
|
+
MixpanelLib.prototype._start_recording_on_event = function(event_name, properties) {
|
|
479
|
+
return this.recorderManager.startRecordingOnEvent(event_name, properties);
|
|
480
|
+
};
|
|
481
|
+
|
|
518
482
|
MixpanelLib.prototype.start_session_recording = function () {
|
|
519
|
-
this._check_and_start_session_recording(true);
|
|
483
|
+
return this._check_and_start_session_recording(true);
|
|
520
484
|
};
|
|
521
485
|
|
|
522
486
|
MixpanelLib.prototype.stop_session_recording = function () {
|
|
523
|
-
|
|
524
|
-
return this._recorder['stopRecording']();
|
|
525
|
-
}
|
|
526
|
-
return Promise.resolve();
|
|
487
|
+
return this.recorderManager.stopSessionRecording();
|
|
527
488
|
};
|
|
528
489
|
|
|
529
490
|
MixpanelLib.prototype.pause_session_recording = function () {
|
|
530
|
-
|
|
531
|
-
return this._recorder['pauseRecording']();
|
|
532
|
-
}
|
|
533
|
-
return Promise.resolve();
|
|
491
|
+
return this.recorderManager.pauseSessionRecording();
|
|
534
492
|
};
|
|
535
493
|
|
|
536
494
|
MixpanelLib.prototype.resume_session_recording = function () {
|
|
537
|
-
|
|
538
|
-
return this._recorder['resumeRecording']();
|
|
539
|
-
}
|
|
540
|
-
return Promise.resolve();
|
|
495
|
+
return this.recorderManager.resumeSessionRecording();
|
|
541
496
|
};
|
|
542
497
|
|
|
543
498
|
MixpanelLib.prototype.is_recording_heatmap_data = function () {
|
|
544
|
-
return this.
|
|
499
|
+
return this.recorderManager.isRecordingHeatmapData();
|
|
545
500
|
};
|
|
546
501
|
|
|
547
502
|
MixpanelLib.prototype.get_session_recording_properties = function () {
|
|
548
|
-
|
|
549
|
-
var replay_id = this._get_session_replay_id();
|
|
550
|
-
if (replay_id) {
|
|
551
|
-
props['$mp_replay_id'] = replay_id;
|
|
552
|
-
}
|
|
553
|
-
return props;
|
|
503
|
+
return this.recorderManager.getSessionRecordingProperties();
|
|
554
504
|
};
|
|
555
505
|
|
|
556
506
|
MixpanelLib.prototype.get_session_replay_url = function () {
|
|
557
|
-
|
|
558
|
-
var replay_id = this._get_session_replay_id();
|
|
559
|
-
if (replay_id) {
|
|
560
|
-
var query_params = _.HTTPBuildQuery({
|
|
561
|
-
'replay_id': replay_id,
|
|
562
|
-
'distinct_id': this.get_distinct_id(),
|
|
563
|
-
'token': this.get_config('token')
|
|
564
|
-
});
|
|
565
|
-
replay_url = 'https://mixpanel.com/projects/replay-redirect?' + query_params;
|
|
566
|
-
}
|
|
567
|
-
return replay_url;
|
|
568
|
-
};
|
|
569
|
-
|
|
570
|
-
MixpanelLib.prototype._get_session_replay_id = function () {
|
|
571
|
-
var replay_id = null;
|
|
572
|
-
if (this._recorder) {
|
|
573
|
-
replay_id = this._recorder['replayId'];
|
|
574
|
-
}
|
|
575
|
-
return replay_id || null;
|
|
507
|
+
return this.recorderManager.getSessionReplayUrl();
|
|
576
508
|
};
|
|
577
509
|
|
|
578
510
|
// "private" public method to reach into the recorder in test cases
|
|
579
511
|
MixpanelLib.prototype.__get_recorder = function () {
|
|
580
|
-
return this.
|
|
512
|
+
return this.recorderManager.getRecorder();
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
// "private" public method to get session recording init promise in test cases
|
|
516
|
+
MixpanelLib.prototype.__get_recording_init_promise = function () {
|
|
517
|
+
return this.__session_recording_init_promise;
|
|
581
518
|
};
|
|
582
519
|
|
|
583
520
|
// Private methods
|
|
@@ -835,6 +772,7 @@ MixpanelLib.prototype._send_request = function(url, data, options, callback) {
|
|
|
835
772
|
};
|
|
836
773
|
|
|
837
774
|
MixpanelLib.prototype._fetch_remote_settings = function(mode) {
|
|
775
|
+
var self = this;
|
|
838
776
|
var disableRecordingIfStrict = function() {
|
|
839
777
|
if (mode === 'strict') {
|
|
840
778
|
self.set_config({'record_sessions_percent': 0});
|
|
@@ -855,7 +793,6 @@ MixpanelLib.prototype._fetch_remote_settings = function(mode) {
|
|
|
855
793
|
};
|
|
856
794
|
var query_string = _.HTTPBuildQuery(request_params);
|
|
857
795
|
var full_url = settings_endpoint + '?' + query_string;
|
|
858
|
-
var self = this;
|
|
859
796
|
|
|
860
797
|
var abortController = new AbortController();
|
|
861
798
|
var timeout_id = setTimeout(function() {
|
|
@@ -1047,6 +984,34 @@ MixpanelLib.prototype.push = function(item) {
|
|
|
1047
984
|
this._execute_array([item]);
|
|
1048
985
|
};
|
|
1049
986
|
|
|
987
|
+
/**
|
|
988
|
+
* Enables events on the Mixpanel object. If passed no arguments,
|
|
989
|
+
* this function enable tracking of all events. If passed an
|
|
990
|
+
* array of event names, those events will be enabled, but other
|
|
991
|
+
* existing disabled events will continue to be not tracked.
|
|
992
|
+
*
|
|
993
|
+
* @param {Array} [events] An array of event names to enable
|
|
994
|
+
*/
|
|
995
|
+
MixpanelLib.prototype.enable = function(events) {
|
|
996
|
+
var keys, new_disabled_events, i, j;
|
|
997
|
+
|
|
998
|
+
if (typeof(events) === 'undefined') {
|
|
999
|
+
this._flags.disable_all_events = false;
|
|
1000
|
+
} else {
|
|
1001
|
+
keys = {};
|
|
1002
|
+
new_disabled_events = [];
|
|
1003
|
+
for (i = 0; i < events.length; i++) {
|
|
1004
|
+
keys[events[i]] = true;
|
|
1005
|
+
}
|
|
1006
|
+
for (j = 0; j < this.__disabled_events.length; j++) {
|
|
1007
|
+
if (!keys[this.__disabled_events[j]]) {
|
|
1008
|
+
new_disabled_events.push(this.__disabled_events[j]);
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
this.__disabled_events = new_disabled_events;
|
|
1012
|
+
}
|
|
1013
|
+
};
|
|
1014
|
+
|
|
1050
1015
|
/**
|
|
1051
1016
|
* Disable events on the Mixpanel object. If passed no arguments,
|
|
1052
1017
|
* this function disables tracking of any event. If passed an
|
|
@@ -1220,6 +1185,8 @@ MixpanelLib.prototype.track = addOptOutCheckMixpanelLib(function(event_name, pro
|
|
|
1220
1185
|
this.report_error('Invalid value for property_blacklist config: ' + property_blacklist);
|
|
1221
1186
|
}
|
|
1222
1187
|
|
|
1188
|
+
this._start_recording_on_event(event_name, properties);
|
|
1189
|
+
|
|
1223
1190
|
var data = {
|
|
1224
1191
|
'event': event_name,
|
|
1225
1192
|
'properties': properties
|
|
@@ -1233,6 +1200,11 @@ MixpanelLib.prototype.track = addOptOutCheckMixpanelLib(function(event_name, pro
|
|
|
1233
1200
|
send_request_options: options
|
|
1234
1201
|
}, callback);
|
|
1235
1202
|
|
|
1203
|
+
// Check for first-time event matches
|
|
1204
|
+
if (this.flags && this.flags.checkFirstTimeEvents) {
|
|
1205
|
+
this.flags.checkFirstTimeEvents(event_name, properties);
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1236
1208
|
return ret;
|
|
1237
1209
|
});
|
|
1238
1210
|
|
|
@@ -2423,6 +2395,7 @@ MixpanelLib.prototype.remove_hook = function(hook_name, hook_fn) {
|
|
|
2423
2395
|
// MixpanelLib Exports
|
|
2424
2396
|
MixpanelLib.prototype['init'] = MixpanelLib.prototype.init;
|
|
2425
2397
|
MixpanelLib.prototype['reset'] = MixpanelLib.prototype.reset;
|
|
2398
|
+
MixpanelLib.prototype['enable'] = MixpanelLib.prototype.enable;
|
|
2426
2399
|
MixpanelLib.prototype['disable'] = MixpanelLib.prototype.disable;
|
|
2427
2400
|
MixpanelLib.prototype['time_event'] = MixpanelLib.prototype.time_event;
|
|
2428
2401
|
MixpanelLib.prototype['track'] = MixpanelLib.prototype.track;
|
|
@@ -2466,6 +2439,7 @@ MixpanelLib.prototype['DEFAULT_API_ROUTES'] = DEFAULT_API_ROUTES
|
|
|
2466
2439
|
|
|
2467
2440
|
// Exports intended only for testing
|
|
2468
2441
|
MixpanelLib.prototype['__get_recorder'] = MixpanelLib.prototype.__get_recorder;
|
|
2442
|
+
MixpanelLib.prototype['__get_recording_init_promise'] = MixpanelLib.prototype.__get_recording_init_promise;
|
|
2469
2443
|
|
|
2470
2444
|
// MixpanelPersistence Exports
|
|
2471
2445
|
MixpanelPersistence.prototype['properties'] = MixpanelPersistence.prototype.properties;
|
package/src/recorder/index.js
CHANGED
package/src/recorder/recorder.js
CHANGED
|
@@ -125,8 +125,12 @@ MixpanelRecorder.prototype.resetRecording = function () {
|
|
|
125
125
|
this.startRecording({shouldStopBatcher: true});
|
|
126
126
|
};
|
|
127
127
|
|
|
128
|
+
MixpanelRecorder.prototype.isRecording = function () {
|
|
129
|
+
return this.activeRecording && !this.activeRecording.isRrwebStopped();
|
|
130
|
+
};
|
|
131
|
+
|
|
128
132
|
MixpanelRecorder.prototype.getActiveReplayId = function () {
|
|
129
|
-
if (this.
|
|
133
|
+
if (this.isRecording()) {
|
|
130
134
|
return this.activeRecording.replayId;
|
|
131
135
|
} else {
|
|
132
136
|
return null;
|