Skip to content

Commit

Permalink
Merge pull request #217 from mixpanel/2.55.1-rc
Browse files Browse the repository at this point in the history
2.55.1 rc
  • Loading branch information
tdumitrescu authored Aug 27, 2024
2 parents 93df8eb + cb938d7 commit f4ceda4
Show file tree
Hide file tree
Showing 21 changed files with 889 additions and 319 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
**2.55.1** (27 Aug 2024)
- Adds a minimum recording length option for session recording
- Fixes and improvements for session recording batcher to support offline queueing and retry

**2.55.0** (2 Aug 2024)
- Added new build to support native JavaScript modules

Expand Down
27 changes: 21 additions & 6 deletions dist/mixpanel-core.cjs.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

var Config = {
DEBUG: false,
LIB_VERSION: '2.55.0'
LIB_VERSION: '2.55.1'
};

/* eslint camelcase: "off", eqeqeq: "off" */
Expand All @@ -14,7 +14,7 @@ if (typeof(window) === 'undefined') {
hostname: ''
};
win = {
navigator: { userAgent: '' },
navigator: { userAgent: '', onLine: true },
document: {
location: loc,
referrer: ''
Expand Down Expand Up @@ -968,7 +968,7 @@ _.HTTPBuildQuery = function(formdata, arg_separator) {
_.getQueryParam = function(url, param) {
// Expects a raw URL

param = param.replace(/[[]/, '\\[').replace(/[\]]/, '\\]');
param = param.replace(/[[]/g, '\\[').replace(/[\]]/g, '\\]');
var regexS = '[\\?&]' + param + '=([^&#]*)',
regex = new RegExp(regexS),
results = regex.exec(url);
Expand Down Expand Up @@ -1425,8 +1425,8 @@ _.dom_query = (function() {
};
})();

var CAMPAIGN_KEYWORDS = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term'];
var CLICK_IDS = ['dclid', 'fbclid', 'gclid', 'ko_click_id', 'li_fat_id', 'msclkid', 'ttclid', 'twclid', 'wbraid'];
var CAMPAIGN_KEYWORDS = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term', 'utm_id', 'utm_source_platform','utm_campaign_id', 'utm_creative_format', 'utm_marketing_tactic'];
var CLICK_IDS = ['dclid', 'fbclid', 'gclid', 'ko_click_id', 'li_fat_id', 'msclkid', 'sccid', 'ttclid', 'twclid', 'wbraid'];

_.info = {
campaignParams: function(default_value) {
Expand Down Expand Up @@ -1708,6 +1708,15 @@ var extract_domain = function(hostname) {
return matches ? matches[0] : '';
};

/**
* Check whether we have network connection. default to true for browsers that don't support navigator.onLine (IE)
* @returns {boolean}
*/
var isOnline = function() {
var onLine = win.navigator['onLine'];
return _.isUndefined(onLine) || onLine;
};

var JSONStringify = null, JSONParse = null;
if (typeof JSON !== 'undefined') {
JSONStringify = JSON.stringify;
Expand Down Expand Up @@ -2523,7 +2532,12 @@ RequestBatcher.prototype.flush = function(options) {
this.flush();
} else if (
_.isObject(res) &&
(res.httpStatusCode >= 500 || res.httpStatusCode === 429 || res.error === 'timeout')
(
res.httpStatusCode >= 500
|| res.httpStatusCode === 429
|| (res.httpStatusCode <= 0 && !isOnline())
|| res.error === 'timeout'
)
) {
// network or API error, or 429 Too Many Requests, retry
var retryMS = this.flushInterval * 2;
Expand Down Expand Up @@ -4247,6 +4261,7 @@ var DEFAULT_CONFIG = {
'record_mask_text_class': new RegExp('^(mp-mask|fs-mask|amp-mask|rr-mask|ph-mask)$'),
'record_mask_text_selector': '*',
'record_max_ms': MAX_RECORDING_MS,
'record_min_ms': 0,
'record_sessions_percent': 0,
'recorder_src': 'https://cdn.mxpnl.com/libs/mixpanel-recorder.min.js'
};
Expand Down
74 changes: 55 additions & 19 deletions dist/mixpanel-recorder.js
Original file line number Diff line number Diff line change
Expand Up @@ -4510,7 +4510,7 @@

var Config = {
DEBUG: false,
LIB_VERSION: '2.55.0'
LIB_VERSION: '2.55.1'
};

/* eslint camelcase: "off", eqeqeq: "off" */
Expand All @@ -4522,7 +4522,7 @@
hostname: ''
};
win = {
navigator: { userAgent: '' },
navigator: { userAgent: '', onLine: true },
document: {
location: loc,
referrer: ''
Expand All @@ -4536,6 +4536,8 @@

// Maximum allowed session recording length
var MAX_RECORDING_MS = 24 * 60 * 60 * 1000; // 24 hours
// Maximum allowed value for minimum session recording length
var MAX_VALUE_FOR_MIN_RECORDING_MS = 8 * 1000; // 8 seconds

/*
* Saved references to long variable names, so that closure compiler can
Expand Down Expand Up @@ -5447,7 +5449,7 @@
_.getQueryParam = function(url, param) {
// Expects a raw URL

param = param.replace(/[[]/, '\\[').replace(/[\]]/, '\\]');
param = param.replace(/[[]/g, '\\[').replace(/[\]]/g, '\\]');
var regexS = '[\\?&]' + param + '=([^&#]*)',
regex = new RegExp(regexS),
results = regex.exec(url);
Expand Down Expand Up @@ -5904,8 +5906,8 @@
};
})();

var CAMPAIGN_KEYWORDS = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term'];
var CLICK_IDS = ['dclid', 'fbclid', 'gclid', 'ko_click_id', 'li_fat_id', 'msclkid', 'ttclid', 'twclid', 'wbraid'];
var CAMPAIGN_KEYWORDS = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term', 'utm_id', 'utm_source_platform','utm_campaign_id', 'utm_creative_format', 'utm_marketing_tactic'];
var CLICK_IDS = ['dclid', 'fbclid', 'gclid', 'ko_click_id', 'li_fat_id', 'msclkid', 'sccid', 'ttclid', 'twclid', 'wbraid'];

_.info = {
campaignParams: function(default_value) {
Expand Down Expand Up @@ -6187,6 +6189,15 @@
return matches ? matches[0] : '';
};

/**
* Check whether we have network connection. default to true for browsers that don't support navigator.onLine (IE)
* @returns {boolean}
*/
var isOnline = function() {
var onLine = win.navigator['onLine'];
return _.isUndefined(onLine) || onLine;
};

var JSONStringify = null, JSONParse = null;
if (typeof JSON !== 'undefined') {
JSONStringify = JSON.stringify;
Expand Down Expand Up @@ -7018,7 +7029,12 @@
this.flush();
} else if (
_.isObject(res) &&
(res.httpStatusCode >= 500 || res.httpStatusCode === 429 || res.error === 'timeout')
(
res.httpStatusCode >= 500
|| res.httpStatusCode === 429
|| (res.httpStatusCode <= 0 && !isOnline())
|| res.error === 'timeout'
)
) {
// network or API error, or 429 Too Many Requests, retry
var retryMS = this.flushInterval * 2;
Expand Down Expand Up @@ -7169,6 +7185,7 @@
this.maxTimeoutId = null;

this.recordMaxMs = MAX_RECORDING_MS;
this.recordMinMs = 0;
this._initBatcher();
};

Expand Down Expand Up @@ -7200,16 +7217,24 @@
logger.critical('record_max_ms cannot be greater than ' + MAX_RECORDING_MS + 'ms. Capping value.');
}

this.recordMinMs = this.get_config('record_min_ms');
if (this.recordMinMs > MAX_VALUE_FOR_MIN_RECORDING_MS) {
this.recordMinMs = MAX_VALUE_FOR_MIN_RECORDING_MS;
logger.critical('record_min_ms cannot be greater than ' + MAX_VALUE_FOR_MIN_RECORDING_MS + 'ms. Capping value.');
}

this.recEvents = [];
this.seqNo = 0;
this.replayStartTime = null;
this.replayStartTime = new Date().getTime();

this.replayId = _.UUID();

if (shouldStopBatcher) {
// this is the case when we're starting recording after a reset
if (shouldStopBatcher || this.recordMinMs > 0) {
// the primary case for shouldStopBatcher is when we're starting recording after a reset
// and don't want to send anything over the network until there's
// actual user activity
// this also applies if the minimum recording length has not been hit yet
// so that we don't send data until we know the recording will be long enough
this.batcher.stop();
} else {
this.batcher.start();
Expand All @@ -7223,19 +7248,24 @@
}, this), this.get_config('record_idle_timeout_ms'));
}, this);

var blockSelector = this.get_config('record_block_selector');
if (blockSelector === '' || blockSelector === null) {
blockSelector = undefined;
}

this._stopRecording = record({
'emit': _.bind(function (ev) {
this.batcher.enqueue(ev);
if (isUserEvent(ev)) {
if (this.batcher.stopped) {
if (this.batcher.stopped && new Date().getTime() - this.replayStartTime >= this.recordMinMs) {
// start flushing again after user activity
this.batcher.start();
}
resetIdleTimeout();
}
}, this),
'blockClass': this.get_config('record_block_class'),
'blockSelector': this.get_config('record_block_selector'),
'blockSelector': blockSelector,
'collectFonts': this.get_config('record_collect_fonts'),
'inlineImages': this.get_config('record_inline_images'),
'maskAllInputs': true,
Expand Down Expand Up @@ -7289,14 +7319,14 @@
}
};

MixpanelRecorder.prototype._sendRequest = function(reqParams, reqBody, callback) {
MixpanelRecorder.prototype._sendRequest = function(currentReplayId, reqParams, reqBody, callback) {
var onSuccess = _.bind(function (response, responseBody) {
// Increment sequence counter only if the request was successful to guarantee ordering.
// RequestBatcher will always flush the next batch after the previous one succeeds.
if (response.status === 200) {
// extra check to see if the replay ID has changed so that we don't increment the seqNo on the wrong replay
if (response.status === 200 && this.replayId === currentReplayId) {
this.seqNo++;
}

callback({
status: 0,
httpStatusCode: response.status,
Expand All @@ -7319,17 +7349,23 @@
callback({error: error});
});
}).catch(function (error) {
callback({error: error});
callback({error: error, httpStatusCode: 0});
});
};

MixpanelRecorder.prototype._flushEvents = addOptOutCheckMixpanelLib(function (data, options, callback) {
const numEvents = data.length;

if (numEvents > 0) {
var replayId = this.replayId;
// each rrweb event has a timestamp - leverage those to get time properties
var batchStartTime = data[0].timestamp;
if (this.seqNo === 0) {
if (this.seqNo === 0 || !this.replayStartTime) {
// extra safety net so that we don't send a null replay start time
if (this.seqNo !== 0) {
this.reportError('Replay start time not set but seqNo is not 0. Using current batch start time as a fallback.');
}

this.replayStartTime = batchStartTime;
}
var replayLengthMs = data[numEvents - 1].timestamp - this.replayStartTime;
Expand All @@ -7338,7 +7374,7 @@
'distinct_id': String(this._mixpanel.get_distinct_id()),
'seq': this.seqNo,
'batch_start_time': batchStartTime / 1000,
'replay_id': this.replayId,
'replay_id': replayId,
'replay_length_ms': replayLengthMs,
'replay_start_time': this.replayStartTime / 1000
};
Expand All @@ -7361,11 +7397,11 @@
.blob()
.then(_.bind(function(compressedBlob) {
reqParams['format'] = 'gzip';
this._sendRequest(reqParams, compressedBlob, callback);
this._sendRequest(replayId, reqParams, compressedBlob, callback);
}, this));
} else {
reqParams['format'] = 'body';
this._sendRequest(reqParams, eventsJson, callback);
this._sendRequest(replayId, reqParams, eventsJson, callback);
}
}
});
Expand Down
18 changes: 9 additions & 9 deletions dist/mixpanel-recorder.min.js

Large diffs are not rendered by default.

27 changes: 21 additions & 6 deletions dist/mixpanel-with-async-recorder.cjs.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

var Config = {
DEBUG: false,
LIB_VERSION: '2.55.0'
LIB_VERSION: '2.55.1'
};

/* eslint camelcase: "off", eqeqeq: "off" */
Expand All @@ -14,7 +14,7 @@ if (typeof(window) === 'undefined') {
hostname: ''
};
win = {
navigator: { userAgent: '' },
navigator: { userAgent: '', onLine: true },
document: {
location: loc,
referrer: ''
Expand Down Expand Up @@ -968,7 +968,7 @@ _.HTTPBuildQuery = function(formdata, arg_separator) {
_.getQueryParam = function(url, param) {
// Expects a raw URL

param = param.replace(/[[]/, '\\[').replace(/[\]]/, '\\]');
param = param.replace(/[[]/g, '\\[').replace(/[\]]/g, '\\]');
var regexS = '[\\?&]' + param + '=([^&#]*)',
regex = new RegExp(regexS),
results = regex.exec(url);
Expand Down Expand Up @@ -1425,8 +1425,8 @@ _.dom_query = (function() {
};
})();

var CAMPAIGN_KEYWORDS = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term'];
var CLICK_IDS = ['dclid', 'fbclid', 'gclid', 'ko_click_id', 'li_fat_id', 'msclkid', 'ttclid', 'twclid', 'wbraid'];
var CAMPAIGN_KEYWORDS = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term', 'utm_id', 'utm_source_platform','utm_campaign_id', 'utm_creative_format', 'utm_marketing_tactic'];
var CLICK_IDS = ['dclid', 'fbclid', 'gclid', 'ko_click_id', 'li_fat_id', 'msclkid', 'sccid', 'ttclid', 'twclid', 'wbraid'];

_.info = {
campaignParams: function(default_value) {
Expand Down Expand Up @@ -1708,6 +1708,15 @@ var extract_domain = function(hostname) {
return matches ? matches[0] : '';
};

/**
* Check whether we have network connection. default to true for browsers that don't support navigator.onLine (IE)
* @returns {boolean}
*/
var isOnline = function() {
var onLine = win.navigator['onLine'];
return _.isUndefined(onLine) || onLine;
};

var JSONStringify = null, JSONParse = null;
if (typeof JSON !== 'undefined') {
JSONStringify = JSON.stringify;
Expand Down Expand Up @@ -2523,7 +2532,12 @@ RequestBatcher.prototype.flush = function(options) {
this.flush();
} else if (
_.isObject(res) &&
(res.httpStatusCode >= 500 || res.httpStatusCode === 429 || res.error === 'timeout')
(
res.httpStatusCode >= 500
|| res.httpStatusCode === 429
|| (res.httpStatusCode <= 0 && !isOnline())
|| res.error === 'timeout'
)
) {
// network or API error, or 429 Too Many Requests, retry
var retryMS = this.flushInterval * 2;
Expand Down Expand Up @@ -4247,6 +4261,7 @@ var DEFAULT_CONFIG = {
'record_mask_text_class': new RegExp('^(mp-mask|fs-mask|amp-mask|rr-mask|ph-mask)$'),
'record_mask_text_selector': '*',
'record_max_ms': MAX_RECORDING_MS,
'record_min_ms': 0,
'record_sessions_percent': 0,
'recorder_src': 'https://cdn.mxpnl.com/libs/mixpanel-recorder.min.js'
};
Expand Down
Loading

0 comments on commit f4ceda4

Please sign in to comment.