@openreplay/tracker 17.0.1 → 17.1.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/dist/cjs/entry.js +1061 -24
- package/dist/cjs/entry.js.map +1 -1
- package/dist/cjs/index.js +1059 -24
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/main/app/index.d.ts +1 -0
- package/dist/cjs/main/entry.d.ts +5 -4
- package/dist/cjs/main/index.d.ts +17 -5
- package/dist/cjs/main/modules/analytics/batcher.d.ts +46 -0
- package/dist/cjs/main/modules/analytics/constantProperties.d.ts +53 -0
- package/dist/cjs/main/modules/analytics/demo.d.ts +0 -0
- package/dist/cjs/main/modules/analytics/events.d.ts +37 -0
- package/dist/cjs/main/modules/analytics/index.d.ts +73 -0
- package/dist/cjs/main/modules/analytics/people.d.ts +51 -0
- package/dist/cjs/main/modules/analytics/types.d.ts +32 -0
- package/dist/cjs/main/modules/analytics/utils.d.ts +19 -0
- package/dist/lib/entry.js +1060 -25
- package/dist/lib/entry.js.map +1 -1
- package/dist/lib/index.js +1059 -24
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/main/app/index.d.ts +1 -0
- package/dist/lib/main/entry.d.ts +5 -4
- package/dist/lib/main/index.d.ts +17 -5
- package/dist/lib/main/modules/analytics/batcher.d.ts +46 -0
- package/dist/lib/main/modules/analytics/constantProperties.d.ts +53 -0
- package/dist/lib/main/modules/analytics/demo.d.ts +0 -0
- package/dist/lib/main/modules/analytics/events.d.ts +37 -0
- package/dist/lib/main/modules/analytics/index.d.ts +73 -0
- package/dist/lib/main/modules/analytics/people.d.ts +51 -0
- package/dist/lib/main/modules/analytics/types.d.ts +32 -0
- package/dist/lib/main/modules/analytics/utils.d.ts +19 -0
- package/dist/types/main/app/index.d.ts +1 -0
- package/dist/types/main/entry.d.ts +5 -4
- package/dist/types/main/index.d.ts +17 -5
- package/dist/types/main/modules/analytics/batcher.d.ts +46 -0
- package/dist/types/main/modules/analytics/constantProperties.d.ts +53 -0
- package/dist/types/main/modules/analytics/demo.d.ts +0 -0
- package/dist/types/main/modules/analytics/events.d.ts +37 -0
- package/dist/types/main/modules/analytics/index.d.ts +73 -0
- package/dist/types/main/modules/analytics/people.d.ts +51 -0
- package/dist/types/main/modules/analytics/types.d.ts +32 -0
- package/dist/types/main/modules/analytics/utils.d.ts +19 -0
- package/package.json +3 -3
package/dist/cjs/entry.js
CHANGED
|
@@ -3894,12 +3894,12 @@ function getInlineOptions(mode, logger) {
|
|
|
3894
3894
|
case InlineCssMode.Unset:
|
|
3895
3895
|
const isLocalhost = /^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?\/?/.test(window.location.href);
|
|
3896
3896
|
if (isLocalhost) {
|
|
3897
|
-
logger(`Enabling InlineCssMode by default on localhost to preserve css styles, refer to ${localhostStylesDoc} for details, set InlineCssMode to 0 to skip this behavior`);
|
|
3897
|
+
logger(`Enabling InlineCssMode.PlainFetched by default on localhost to preserve css styles, refer to ${localhostStylesDoc} for details, set InlineCssMode to 0 to skip this behavior`);
|
|
3898
3898
|
return {
|
|
3899
3899
|
inlineRemoteCss: true,
|
|
3900
3900
|
inlinerOptions: {
|
|
3901
|
-
forceFetch:
|
|
3902
|
-
forcePlain:
|
|
3901
|
+
forceFetch: true,
|
|
3902
|
+
forcePlain: true,
|
|
3903
3903
|
},
|
|
3904
3904
|
};
|
|
3905
3905
|
}
|
|
@@ -4383,6 +4383,9 @@ const proto = {
|
|
|
4383
4383
|
startIframe: 'start tracker inside frame',
|
|
4384
4384
|
// checking updates
|
|
4385
4385
|
polling: 'hello-how-are-you-im-under-the-water-please-help-me',
|
|
4386
|
+
// happens if tab is old and has outdated token but
|
|
4387
|
+
// not communicating with backend to update it (for whatever reason)
|
|
4388
|
+
reset: 'reset-your-session-please',
|
|
4386
4389
|
};
|
|
4387
4390
|
class App {
|
|
4388
4391
|
constructor(projectKey, sessionToken, options, signalError, insideIframe) {
|
|
@@ -4399,7 +4402,7 @@ class App {
|
|
|
4399
4402
|
this.stopCallbacks = [];
|
|
4400
4403
|
this.commitCallbacks = [];
|
|
4401
4404
|
this.activityState = ActivityState.NotActive;
|
|
4402
|
-
this.version = '17.0
|
|
4405
|
+
this.version = '17.1.0'; // TODO: version compatability check inside each plugin.
|
|
4403
4406
|
this.socketMode = false;
|
|
4404
4407
|
this.compressionThreshold = 24 * 1000;
|
|
4405
4408
|
this.bc = null;
|
|
@@ -4633,6 +4636,19 @@ class App {
|
|
|
4633
4636
|
}
|
|
4634
4637
|
};
|
|
4635
4638
|
this.startTimeout = null;
|
|
4639
|
+
this.restart = () => {
|
|
4640
|
+
this.stop(false);
|
|
4641
|
+
this.waitStatus(ActivityState.NotActive).then(() => {
|
|
4642
|
+
this.allowAppStart();
|
|
4643
|
+
this.start(this.prevOpts, true)
|
|
4644
|
+
.then((r) => {
|
|
4645
|
+
this.debug.info('Session restart', r);
|
|
4646
|
+
})
|
|
4647
|
+
.catch((e) => {
|
|
4648
|
+
this.debug.error('Session restart failed', e);
|
|
4649
|
+
});
|
|
4650
|
+
});
|
|
4651
|
+
};
|
|
4636
4652
|
this.send = (message, urgent = false) => {
|
|
4637
4653
|
if (this.activityState === ActivityState.NotActive) {
|
|
4638
4654
|
return;
|
|
@@ -4794,7 +4810,12 @@ class App {
|
|
|
4794
4810
|
});
|
|
4795
4811
|
this.session.attachUpdateCallback(({ userID, metadata }) => {
|
|
4796
4812
|
if (userID != null) {
|
|
4797
|
-
|
|
4813
|
+
if (!userID ||
|
|
4814
|
+
typeof userID !== 'string' ||
|
|
4815
|
+
userID.trim().length === 0) {
|
|
4816
|
+
this.debug.warn('Invalid userID (must be type string), ignoring.');
|
|
4817
|
+
return;
|
|
4818
|
+
}
|
|
4798
4819
|
this.send(UserID(userID));
|
|
4799
4820
|
}
|
|
4800
4821
|
if (metadata != null) {
|
|
@@ -4869,6 +4890,12 @@ class App {
|
|
|
4869
4890
|
});
|
|
4870
4891
|
}
|
|
4871
4892
|
}
|
|
4893
|
+
if (ev.data.line === proto.reset) {
|
|
4894
|
+
const newToken = ev.data.token;
|
|
4895
|
+
this.debug.log('Received reset signal from another tab');
|
|
4896
|
+
this.session.setSessionToken(newToken, this.projectKey);
|
|
4897
|
+
this.restart();
|
|
4898
|
+
}
|
|
4872
4899
|
};
|
|
4873
4900
|
}
|
|
4874
4901
|
}
|
|
@@ -5441,9 +5468,10 @@ class App {
|
|
|
5441
5468
|
// Reset session metadata only if requested directly
|
|
5442
5469
|
this.session.reset();
|
|
5443
5470
|
}
|
|
5471
|
+
const userId = startOpts.userID ? startOpts.userID.trim() : undefined;
|
|
5444
5472
|
this.session.assign({
|
|
5445
5473
|
// MBTODO: maybe it would make sense to `forceNew` if the `userID` was changed
|
|
5446
|
-
userID:
|
|
5474
|
+
userID: userId || undefined,
|
|
5447
5475
|
metadata: startOpts.metadata,
|
|
5448
5476
|
});
|
|
5449
5477
|
const timestamp = now();
|
|
@@ -5510,6 +5538,12 @@ class App {
|
|
|
5510
5538
|
}
|
|
5511
5539
|
this.delay = delay;
|
|
5512
5540
|
this.session.setSessionToken(token, this.projectKey);
|
|
5541
|
+
if (sessionToken && sessionToken !== token) {
|
|
5542
|
+
this.bc?.postMessage({
|
|
5543
|
+
type: proto.reset,
|
|
5544
|
+
token: token,
|
|
5545
|
+
});
|
|
5546
|
+
}
|
|
5513
5547
|
this.session.setUserInfo({
|
|
5514
5548
|
userBrowser,
|
|
5515
5549
|
userCity,
|
|
@@ -7400,9 +7434,9 @@ function axiosSpy (app, instance, opts, sanitize, stringify) {
|
|
|
7400
7434
|
});
|
|
7401
7435
|
}
|
|
7402
7436
|
function isAxiosError(payload) {
|
|
7403
|
-
return isObject(payload) && payload.isAxiosError === true;
|
|
7437
|
+
return isObject$1(payload) && payload.isAxiosError === true;
|
|
7404
7438
|
}
|
|
7405
|
-
function isObject(thing) {
|
|
7439
|
+
function isObject$1(thing) {
|
|
7406
7440
|
return thing !== null && typeof thing === 'object';
|
|
7407
7441
|
}
|
|
7408
7442
|
|
|
@@ -8816,6 +8850,969 @@ function webAnimations(app, options = {}) {
|
|
|
8816
8850
|
});
|
|
8817
8851
|
}
|
|
8818
8852
|
|
|
8853
|
+
/**
|
|
8854
|
+
* Detects client browser, OS, and device information
|
|
8855
|
+
*/
|
|
8856
|
+
function uaParse(sWindow) {
|
|
8857
|
+
const unknown = '-';
|
|
8858
|
+
// Screen detection
|
|
8859
|
+
let width = 0;
|
|
8860
|
+
let height = 0;
|
|
8861
|
+
let screenSize = '';
|
|
8862
|
+
if (sWindow.screen.width) {
|
|
8863
|
+
width = sWindow.screen.width;
|
|
8864
|
+
height = sWindow.screen.height;
|
|
8865
|
+
screenSize = `${width} x ${height}`;
|
|
8866
|
+
}
|
|
8867
|
+
// Browser detection
|
|
8868
|
+
const nVer = sWindow.navigator.appVersion ?? '0';
|
|
8869
|
+
const nAgt = sWindow.navigator.userAgent ?? 'unknown';
|
|
8870
|
+
let browser = sWindow.navigator.appName ?? "unknown";
|
|
8871
|
+
let version = String(parseFloat(nVer));
|
|
8872
|
+
let nameOffset;
|
|
8873
|
+
let verOffset;
|
|
8874
|
+
let ix;
|
|
8875
|
+
// Browser detection logic
|
|
8876
|
+
if ((verOffset = nAgt.indexOf('YaBrowser')) !== -1) {
|
|
8877
|
+
browser = 'Yandex';
|
|
8878
|
+
version = nAgt.substring(verOffset + 10);
|
|
8879
|
+
}
|
|
8880
|
+
else if ((verOffset = nAgt.indexOf('SamsungBrowser')) !== -1) {
|
|
8881
|
+
browser = 'Samsung';
|
|
8882
|
+
version = nAgt.substring(verOffset + 15);
|
|
8883
|
+
}
|
|
8884
|
+
else if ((verOffset = nAgt.indexOf('UCBrowser')) !== -1) {
|
|
8885
|
+
browser = 'UC Browser';
|
|
8886
|
+
version = nAgt.substring(verOffset + 10);
|
|
8887
|
+
}
|
|
8888
|
+
else if ((verOffset = nAgt.indexOf('OPR')) !== -1) {
|
|
8889
|
+
browser = 'Opera';
|
|
8890
|
+
version = nAgt.substring(verOffset + 4);
|
|
8891
|
+
}
|
|
8892
|
+
else if ((verOffset = nAgt.indexOf('Opera')) !== -1) {
|
|
8893
|
+
browser = 'Opera';
|
|
8894
|
+
version = nAgt.substring(verOffset + 6);
|
|
8895
|
+
if ((verOffset = nAgt.indexOf('Version')) !== -1) {
|
|
8896
|
+
version = nAgt.substring(verOffset + 8);
|
|
8897
|
+
}
|
|
8898
|
+
}
|
|
8899
|
+
else if ((verOffset = nAgt.indexOf('Edge')) !== -1) {
|
|
8900
|
+
browser = 'Microsoft Legacy Edge';
|
|
8901
|
+
version = nAgt.substring(verOffset + 5);
|
|
8902
|
+
}
|
|
8903
|
+
else if ((verOffset = nAgt.indexOf('Edg')) !== -1) {
|
|
8904
|
+
browser = 'Microsoft Edge';
|
|
8905
|
+
version = nAgt.substring(verOffset + 4);
|
|
8906
|
+
}
|
|
8907
|
+
else if ((verOffset = nAgt.indexOf('MSIE')) !== -1) {
|
|
8908
|
+
browser = 'Microsoft Internet Explorer';
|
|
8909
|
+
version = nAgt.substring(verOffset + 5);
|
|
8910
|
+
}
|
|
8911
|
+
else if ((verOffset = nAgt.indexOf('Chrome')) !== -1) {
|
|
8912
|
+
browser = 'Chrome';
|
|
8913
|
+
version = nAgt.substring(verOffset + 7);
|
|
8914
|
+
}
|
|
8915
|
+
else if ((verOffset = nAgt.indexOf('Safari')) !== -1) {
|
|
8916
|
+
browser = 'Safari';
|
|
8917
|
+
version = nAgt.substring(verOffset + 7);
|
|
8918
|
+
if ((verOffset = nAgt.indexOf('Version')) !== -1) {
|
|
8919
|
+
version = nAgt.substring(verOffset + 8);
|
|
8920
|
+
}
|
|
8921
|
+
}
|
|
8922
|
+
else if ((verOffset = nAgt.indexOf('Firefox')) !== -1) {
|
|
8923
|
+
browser = 'Firefox';
|
|
8924
|
+
version = nAgt.substring(verOffset + 8);
|
|
8925
|
+
}
|
|
8926
|
+
else if (nAgt.indexOf('Trident/') !== -1) {
|
|
8927
|
+
browser = 'Microsoft Internet Explorer';
|
|
8928
|
+
version = nAgt.substring(nAgt.indexOf('rv:') + 3);
|
|
8929
|
+
}
|
|
8930
|
+
else if ((nameOffset = nAgt.lastIndexOf(' ') + 1) < (verOffset = nAgt.lastIndexOf('/'))) {
|
|
8931
|
+
browser = nAgt.substring(nameOffset, verOffset);
|
|
8932
|
+
version = nAgt.substring(verOffset + 1);
|
|
8933
|
+
if (browser.toLowerCase() === browser.toUpperCase()) {
|
|
8934
|
+
browser = sWindow.navigator.appName;
|
|
8935
|
+
}
|
|
8936
|
+
}
|
|
8937
|
+
// Trim the version string
|
|
8938
|
+
if ((ix = version.indexOf(';')) !== -1) {
|
|
8939
|
+
version = version.substring(0, ix);
|
|
8940
|
+
}
|
|
8941
|
+
if ((ix = version.indexOf(' ')) !== -1) {
|
|
8942
|
+
version = version.substring(0, ix);
|
|
8943
|
+
}
|
|
8944
|
+
if ((ix = version.indexOf(')')) !== -1) {
|
|
8945
|
+
version = version.substring(0, ix);
|
|
8946
|
+
}
|
|
8947
|
+
let majorVersion = parseInt(version, 10);
|
|
8948
|
+
if (isNaN(majorVersion)) {
|
|
8949
|
+
version = String(parseFloat(nVer));
|
|
8950
|
+
majorVersion = parseInt(nVer, 10);
|
|
8951
|
+
}
|
|
8952
|
+
// Mobile detection
|
|
8953
|
+
const mobile = /Mobile|mini|Fennec|Android|iP(ad|od|hone)/.test(nVer);
|
|
8954
|
+
// Cookie detection
|
|
8955
|
+
let cookieEnabled = sWindow.navigator.cookieEnabled || false;
|
|
8956
|
+
if (typeof navigator.cookieEnabled === 'undefined' && !cookieEnabled) {
|
|
8957
|
+
sWindow.document.cookie = 'testcookie';
|
|
8958
|
+
cookieEnabled = sWindow.document.cookie.indexOf('testcookie') !== -1;
|
|
8959
|
+
}
|
|
8960
|
+
// OS detection
|
|
8961
|
+
let os = unknown;
|
|
8962
|
+
const clientStrings = [
|
|
8963
|
+
{ s: 'Windows 10', r: /(Windows 10.0|Windows NT 10.0)/ },
|
|
8964
|
+
{ s: 'Windows 8.1', r: /(Windows 8.1|Windows NT 6.3)/ },
|
|
8965
|
+
{ s: 'Windows 8', r: /(Windows 8|Windows NT 6.2)/ },
|
|
8966
|
+
{ s: 'Windows 7', r: /(Windows 7|Windows NT 6.1)/ },
|
|
8967
|
+
{ s: 'Windows Vista', r: /Windows NT 6.0/ },
|
|
8968
|
+
{ s: 'Windows Server 2003', r: /Windows NT 5.2/ },
|
|
8969
|
+
{ s: 'Windows XP', r: /(Windows NT 5.1|Windows XP)/ },
|
|
8970
|
+
{ s: 'Windows 2000', r: /(Windows NT 5.0|Windows 2000)/ },
|
|
8971
|
+
{ s: 'Windows ME', r: /(Win 9x 4.90|Windows ME)/ },
|
|
8972
|
+
{ s: 'Windows 98', r: /(Windows 98|Win98)/ },
|
|
8973
|
+
{ s: 'Windows 95', r: /(Windows 95|Win95|Windows_95)/ },
|
|
8974
|
+
{ s: 'Windows NT 4.0', r: /(Windows NT 4.0|WinNT4.0|WinNT|Windows NT)/ },
|
|
8975
|
+
{ s: 'Windows CE', r: /Windows CE/ },
|
|
8976
|
+
{ s: 'Windows 3.11', r: /Win16/ },
|
|
8977
|
+
{ s: 'Android', r: /Android/ },
|
|
8978
|
+
{ s: 'Open BSD', r: /OpenBSD/ },
|
|
8979
|
+
{ s: 'Sun OS', r: /SunOS/ },
|
|
8980
|
+
{ s: 'Chrome OS', r: /CrOS/ },
|
|
8981
|
+
{ s: 'Linux', r: /(Linux|X11(?!.*CrOS))/ },
|
|
8982
|
+
{ s: 'iOS', r: /(iPhone|iPad|iPod)/ },
|
|
8983
|
+
{ s: 'Mac OS X', r: /Mac OS X/ },
|
|
8984
|
+
{ s: 'Mac OS', r: /(Mac OS|MacPPC|MacIntel|Mac_PowerPC|Macintosh)/ },
|
|
8985
|
+
{ s: 'QNX', r: /QNX/ },
|
|
8986
|
+
{ s: 'UNIX', r: /UNIX/ },
|
|
8987
|
+
{ s: 'BeOS', r: /BeOS/ },
|
|
8988
|
+
{ s: 'OS/2', r: /OS\/2/ },
|
|
8989
|
+
{
|
|
8990
|
+
s: 'Search Bot',
|
|
8991
|
+
r: /(nuhk|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask Jeeves\/Teoma|ia_archiver)/,
|
|
8992
|
+
},
|
|
8993
|
+
];
|
|
8994
|
+
// Find matching OS
|
|
8995
|
+
for (const client of clientStrings) {
|
|
8996
|
+
if (client.r.test(nAgt)) {
|
|
8997
|
+
os = client.s;
|
|
8998
|
+
break;
|
|
8999
|
+
}
|
|
9000
|
+
}
|
|
9001
|
+
// OS Version detection
|
|
9002
|
+
let osVersion = unknown;
|
|
9003
|
+
if (/Windows/.test(os)) {
|
|
9004
|
+
const matches = /Windows (.*)/.exec(os);
|
|
9005
|
+
if (matches && matches[1]) {
|
|
9006
|
+
osVersion = matches[1];
|
|
9007
|
+
// Handle Windows 10/11 detection with newer API if available
|
|
9008
|
+
if (osVersion === '10' && 'userAgentData' in sWindow.navigator) {
|
|
9009
|
+
const nav = navigator;
|
|
9010
|
+
if (nav.userAgentData) {
|
|
9011
|
+
nav.userAgentData
|
|
9012
|
+
.getHighEntropyValues(['platformVersion'])
|
|
9013
|
+
.then((ua) => {
|
|
9014
|
+
const version = parseInt(ua.platformVersion.split('.')[0], 10);
|
|
9015
|
+
osVersion = version < 13 ? '10' : '11';
|
|
9016
|
+
})
|
|
9017
|
+
.catch(() => {
|
|
9018
|
+
// ignore errors and keep osVersion as is
|
|
9019
|
+
});
|
|
9020
|
+
}
|
|
9021
|
+
}
|
|
9022
|
+
}
|
|
9023
|
+
os = 'Windows';
|
|
9024
|
+
}
|
|
9025
|
+
// OS version detection for Mac/Android/iOS
|
|
9026
|
+
switch (os) {
|
|
9027
|
+
case 'Mac OS':
|
|
9028
|
+
case 'Mac OS X':
|
|
9029
|
+
case 'Android': {
|
|
9030
|
+
const matches = /(?:Android|Mac OS|Mac OS X|MacPPC|MacIntel|Mac_PowerPC|Macintosh) ([\.\_\d]+)/.exec(nAgt);
|
|
9031
|
+
osVersion = matches && matches[1] ? matches[1] : unknown;
|
|
9032
|
+
break;
|
|
9033
|
+
}
|
|
9034
|
+
case 'iOS': {
|
|
9035
|
+
const matches = /OS (\d+)_(\d+)_?(\d+)?/.exec(nVer);
|
|
9036
|
+
if (matches && matches[1]) {
|
|
9037
|
+
osVersion = `${matches[1]}.${matches[2]}.${parseInt(matches[3] || '0', 10)}`;
|
|
9038
|
+
}
|
|
9039
|
+
break;
|
|
9040
|
+
}
|
|
9041
|
+
}
|
|
9042
|
+
// Return client data
|
|
9043
|
+
return {
|
|
9044
|
+
screen: screenSize,
|
|
9045
|
+
width,
|
|
9046
|
+
height,
|
|
9047
|
+
browser,
|
|
9048
|
+
browserVersion: version,
|
|
9049
|
+
browserMajorVersion: majorVersion,
|
|
9050
|
+
mobile,
|
|
9051
|
+
os,
|
|
9052
|
+
osVersion,
|
|
9053
|
+
cookies: cookieEnabled,
|
|
9054
|
+
};
|
|
9055
|
+
}
|
|
9056
|
+
function isObject(item) {
|
|
9057
|
+
const isNull = item === null;
|
|
9058
|
+
return Boolean(item && typeof item === 'object' && !Array.isArray(item) && !isNull);
|
|
9059
|
+
}
|
|
9060
|
+
function getUTCOffsetString() {
|
|
9061
|
+
const date = new Date();
|
|
9062
|
+
const offsetMinutes = date.getTimezoneOffset();
|
|
9063
|
+
const hours = Math.abs(Math.floor(offsetMinutes / 60));
|
|
9064
|
+
const minutes = Math.abs(offsetMinutes % 60);
|
|
9065
|
+
const sign = offsetMinutes <= 0 ? '+' : '-';
|
|
9066
|
+
const hoursStr = hours.toString().padStart(2, '0');
|
|
9067
|
+
const minutesStr = minutes.toString().padStart(2, '0');
|
|
9068
|
+
return `UTC${sign}${hoursStr}:${minutesStr}`;
|
|
9069
|
+
}
|
|
9070
|
+
|
|
9071
|
+
const refKey = '$__or__initial_ref__$';
|
|
9072
|
+
const distinctIdKey = '$__or__distinct_device_id__$';
|
|
9073
|
+
const utmParamsKey = '$__or__utm_params__$';
|
|
9074
|
+
const superPropKey = '$__or__super_properties__$';
|
|
9075
|
+
const userIdKey = '$__or__user_id__$';
|
|
9076
|
+
const win = 'window' in globalThis
|
|
9077
|
+
? window
|
|
9078
|
+
: {
|
|
9079
|
+
navigator: { userAgent: '' },
|
|
9080
|
+
screen: {},
|
|
9081
|
+
document: {
|
|
9082
|
+
cookie: '',
|
|
9083
|
+
},
|
|
9084
|
+
location: { search: '' },
|
|
9085
|
+
};
|
|
9086
|
+
const doc = 'document' in globalThis ? document : { referrer: '' };
|
|
9087
|
+
const searchEngineList = [
|
|
9088
|
+
'google',
|
|
9089
|
+
'bing',
|
|
9090
|
+
'yahoo',
|
|
9091
|
+
'baidu',
|
|
9092
|
+
'yandex',
|
|
9093
|
+
'duckduckgo',
|
|
9094
|
+
'ecosia',
|
|
9095
|
+
'ask',
|
|
9096
|
+
'aol',
|
|
9097
|
+
'wolframalpha',
|
|
9098
|
+
'startpage',
|
|
9099
|
+
'swisscows',
|
|
9100
|
+
'qwant',
|
|
9101
|
+
'lycos',
|
|
9102
|
+
'dogpile',
|
|
9103
|
+
'info',
|
|
9104
|
+
'teoma',
|
|
9105
|
+
'webcrawler',
|
|
9106
|
+
'naver',
|
|
9107
|
+
'seznam',
|
|
9108
|
+
'perplexity',
|
|
9109
|
+
];
|
|
9110
|
+
class ConstantProperties {
|
|
9111
|
+
constructor(localStorage, sessionStorage) {
|
|
9112
|
+
this.localStorage = localStorage;
|
|
9113
|
+
this.sessionStorage = sessionStorage;
|
|
9114
|
+
this.user_id = null;
|
|
9115
|
+
this.setUserId = (user_id) => {
|
|
9116
|
+
this.user_id = user_id;
|
|
9117
|
+
this.sessionStorage.setItem(userIdKey, user_id ?? '');
|
|
9118
|
+
};
|
|
9119
|
+
this.resetUserId = (hard) => {
|
|
9120
|
+
this.user_id = null;
|
|
9121
|
+
if (hard) {
|
|
9122
|
+
this.deviceId = this.getDistinctDeviceId(true);
|
|
9123
|
+
}
|
|
9124
|
+
};
|
|
9125
|
+
this.getDistinctDeviceId = (force) => {
|
|
9126
|
+
const potentialStored = this.localStorage.getItem(distinctIdKey);
|
|
9127
|
+
if (potentialStored && !force) {
|
|
9128
|
+
return potentialStored;
|
|
9129
|
+
}
|
|
9130
|
+
else {
|
|
9131
|
+
const distinctId = `${Math.random().toString(36).slice(2)}-${Math.random().toString(36).slice(2)}-${Math.random().toString(36).slice(2)}`;
|
|
9132
|
+
this.localStorage.setItem(distinctIdKey, distinctId);
|
|
9133
|
+
return distinctId;
|
|
9134
|
+
}
|
|
9135
|
+
};
|
|
9136
|
+
this.getReferrer = () => {
|
|
9137
|
+
const potentialStored = this.sessionStorage.getItem(refKey);
|
|
9138
|
+
if (potentialStored) {
|
|
9139
|
+
return potentialStored;
|
|
9140
|
+
}
|
|
9141
|
+
else {
|
|
9142
|
+
const ref = doc.referrer;
|
|
9143
|
+
this.sessionStorage.setItem(refKey, ref);
|
|
9144
|
+
return ref;
|
|
9145
|
+
}
|
|
9146
|
+
};
|
|
9147
|
+
this.parseUTM = () => {
|
|
9148
|
+
const potentialStored = this.sessionStorage.getItem(utmParamsKey);
|
|
9149
|
+
if (potentialStored) {
|
|
9150
|
+
const obj = JSON.parse(potentialStored);
|
|
9151
|
+
this.utmSource = obj.utm_source;
|
|
9152
|
+
this.utmMedium = obj.utm_medium;
|
|
9153
|
+
this.utmCampaign = obj.utm_campaign;
|
|
9154
|
+
}
|
|
9155
|
+
else {
|
|
9156
|
+
const searchParams = new URLSearchParams(win.location.search);
|
|
9157
|
+
this.utmSource = searchParams.get('utm_source') || null;
|
|
9158
|
+
this.utmMedium = searchParams.get('utm_medium') || null;
|
|
9159
|
+
this.utmCampaign = searchParams.get('utm_campaign') || null;
|
|
9160
|
+
const obj = {
|
|
9161
|
+
utm_source: this.utmSource,
|
|
9162
|
+
utm_medium: this.utmMedium,
|
|
9163
|
+
utm_campaign: this.utmCampaign,
|
|
9164
|
+
};
|
|
9165
|
+
this.sessionStorage.setItem(utmParamsKey, JSON.stringify(obj));
|
|
9166
|
+
}
|
|
9167
|
+
};
|
|
9168
|
+
this.getSearchEngine = (ref) => {
|
|
9169
|
+
for (const searchEngine of searchEngineList) {
|
|
9170
|
+
if (ref.includes(searchEngine)) {
|
|
9171
|
+
return searchEngine;
|
|
9172
|
+
}
|
|
9173
|
+
}
|
|
9174
|
+
return null;
|
|
9175
|
+
};
|
|
9176
|
+
this.getSuperProperties = () => {
|
|
9177
|
+
const potentialStored = this.localStorage.getItem(superPropKey);
|
|
9178
|
+
if (potentialStored) {
|
|
9179
|
+
return JSON.parse(potentialStored);
|
|
9180
|
+
}
|
|
9181
|
+
else {
|
|
9182
|
+
return {};
|
|
9183
|
+
}
|
|
9184
|
+
};
|
|
9185
|
+
this.saveSuperProperties = (props) => {
|
|
9186
|
+
this.localStorage.setItem(superPropKey, JSON.stringify(props));
|
|
9187
|
+
};
|
|
9188
|
+
this.clearSuperProperties = () => {
|
|
9189
|
+
this.localStorage.setItem(superPropKey, JSON.stringify({}));
|
|
9190
|
+
};
|
|
9191
|
+
const { width, height, browser, browserVersion, browserMajorVersion, os, osVersion, mobile } = uaParse(win);
|
|
9192
|
+
const storedUserId = this.sessionStorage.getItem(userIdKey);
|
|
9193
|
+
if (storedUserId) {
|
|
9194
|
+
this.user_id = storedUserId;
|
|
9195
|
+
}
|
|
9196
|
+
this.os = os;
|
|
9197
|
+
this.osVersion = osVersion;
|
|
9198
|
+
this.browser = `${browser}`;
|
|
9199
|
+
this.browserVersion = `${browserVersion} (${browserMajorVersion})`;
|
|
9200
|
+
this.platform = mobile ? 'mobile' : 'desktop';
|
|
9201
|
+
this.screenHeight = height;
|
|
9202
|
+
this.screenWidth = width;
|
|
9203
|
+
this.initialReferrer = this.getReferrer();
|
|
9204
|
+
this.deviceId = this.getDistinctDeviceId();
|
|
9205
|
+
this.searchEngine = this.getSearchEngine(this.initialReferrer);
|
|
9206
|
+
this.parseUTM();
|
|
9207
|
+
}
|
|
9208
|
+
get all() {
|
|
9209
|
+
return {
|
|
9210
|
+
os: this.os,
|
|
9211
|
+
os_version: this.osVersion,
|
|
9212
|
+
browser: this.browser,
|
|
9213
|
+
browser_version: this.browserVersion,
|
|
9214
|
+
platform: this.platform,
|
|
9215
|
+
screen_height: this.screenHeight,
|
|
9216
|
+
screen_width: this.screenWidth,
|
|
9217
|
+
initial_referrer: this.initialReferrer,
|
|
9218
|
+
utm_source: this.utmSource,
|
|
9219
|
+
utm_medium: this.utmMedium,
|
|
9220
|
+
utm_campaign: this.utmCampaign,
|
|
9221
|
+
user_id: this.user_id,
|
|
9222
|
+
distinct_id: this.deviceId,
|
|
9223
|
+
sdk_edition: 'web',
|
|
9224
|
+
sdk_version: '17.1.0',
|
|
9225
|
+
timezone: getUTCOffsetString(),
|
|
9226
|
+
search_engine: this.searchEngine,
|
|
9227
|
+
};
|
|
9228
|
+
}
|
|
9229
|
+
get defaultPropertyKeys() {
|
|
9230
|
+
return Object.keys(this.all);
|
|
9231
|
+
}
|
|
9232
|
+
get distinctId() {
|
|
9233
|
+
return this.deviceId;
|
|
9234
|
+
}
|
|
9235
|
+
}
|
|
9236
|
+
|
|
9237
|
+
const mutationTypes = {
|
|
9238
|
+
identity: 'identity',
|
|
9239
|
+
deleteUser: 'delete_user',
|
|
9240
|
+
setProperty: 'set_property',
|
|
9241
|
+
setPropertyOnce: 'set_property_once',
|
|
9242
|
+
appendProperty: 'append_property',
|
|
9243
|
+
appendUniqueProperty: 'append_unique_property',
|
|
9244
|
+
incrementProperty: 'increment_property',
|
|
9245
|
+
};
|
|
9246
|
+
const categories = {
|
|
9247
|
+
people: 'user_actions',
|
|
9248
|
+
events: 'events',
|
|
9249
|
+
};
|
|
9250
|
+
const createEvent = (category, type, timestamp, payload) => {
|
|
9251
|
+
if (category === categories.people) {
|
|
9252
|
+
return {
|
|
9253
|
+
category,
|
|
9254
|
+
data: {
|
|
9255
|
+
type,
|
|
9256
|
+
user_id: payload.user_id,
|
|
9257
|
+
payload: payload.properties,
|
|
9258
|
+
timestamp,
|
|
9259
|
+
},
|
|
9260
|
+
};
|
|
9261
|
+
}
|
|
9262
|
+
else {
|
|
9263
|
+
if (!payload) {
|
|
9264
|
+
throw new Error('Payload is required for event creation');
|
|
9265
|
+
}
|
|
9266
|
+
return {
|
|
9267
|
+
category,
|
|
9268
|
+
data: {
|
|
9269
|
+
name: payload.name,
|
|
9270
|
+
payload: payload.properties,
|
|
9271
|
+
timestamp,
|
|
9272
|
+
},
|
|
9273
|
+
};
|
|
9274
|
+
}
|
|
9275
|
+
};
|
|
9276
|
+
|
|
9277
|
+
const reservedProps = ['properties', 'token', 'timestamp'];
|
|
9278
|
+
class Events {
|
|
9279
|
+
constructor(constantProperties, getTimestamp, batcher) {
|
|
9280
|
+
this.constantProperties = constantProperties;
|
|
9281
|
+
this.getTimestamp = getTimestamp;
|
|
9282
|
+
this.batcher = batcher;
|
|
9283
|
+
this.ownProperties = {};
|
|
9284
|
+
/**
|
|
9285
|
+
* Add event to batch with option to send it immediately,
|
|
9286
|
+
* properties are optional and will not be saved as super prop
|
|
9287
|
+
* */
|
|
9288
|
+
this.sendEvent = (eventName, properties, options) => {
|
|
9289
|
+
// add properties
|
|
9290
|
+
const eventProps = {};
|
|
9291
|
+
if (properties) {
|
|
9292
|
+
if (!isObject(properties)) {
|
|
9293
|
+
throw new Error('Properties must be an object');
|
|
9294
|
+
}
|
|
9295
|
+
Object.entries(properties).forEach(([key, value]) => {
|
|
9296
|
+
if (!this.constantProperties.defaultPropertyKeys.includes(key)) {
|
|
9297
|
+
eventProps[key] = value;
|
|
9298
|
+
}
|
|
9299
|
+
});
|
|
9300
|
+
}
|
|
9301
|
+
const eventPayload = {
|
|
9302
|
+
name: eventName,
|
|
9303
|
+
properties: { ...this.ownProperties, ...eventProps },
|
|
9304
|
+
};
|
|
9305
|
+
const event = createEvent(categories.events, undefined, this.getTimestamp(), eventPayload);
|
|
9306
|
+
if (options?.send_immediately) {
|
|
9307
|
+
void this.batcher.sendImmediately(event);
|
|
9308
|
+
}
|
|
9309
|
+
else {
|
|
9310
|
+
this.batcher.addEvent(event);
|
|
9311
|
+
}
|
|
9312
|
+
};
|
|
9313
|
+
/**
|
|
9314
|
+
* creates super property for all events
|
|
9315
|
+
* */
|
|
9316
|
+
this.setProperty = (nameOrProperties, value) => {
|
|
9317
|
+
let changed = false;
|
|
9318
|
+
if (isObject(nameOrProperties)) {
|
|
9319
|
+
Object.entries(nameOrProperties).forEach(([key, val]) => {
|
|
9320
|
+
if (!this.constantProperties.defaultPropertyKeys.includes(key)) {
|
|
9321
|
+
this.ownProperties[key] = val;
|
|
9322
|
+
changed = true;
|
|
9323
|
+
}
|
|
9324
|
+
});
|
|
9325
|
+
}
|
|
9326
|
+
if (typeof nameOrProperties === 'string' && value !== undefined) {
|
|
9327
|
+
if (!this.constantProperties.defaultPropertyKeys.includes(nameOrProperties)) {
|
|
9328
|
+
this.ownProperties[nameOrProperties] = value;
|
|
9329
|
+
changed = true;
|
|
9330
|
+
}
|
|
9331
|
+
}
|
|
9332
|
+
if (changed) {
|
|
9333
|
+
this.constantProperties.saveSuperProperties(this.ownProperties);
|
|
9334
|
+
}
|
|
9335
|
+
};
|
|
9336
|
+
/**
|
|
9337
|
+
* set super property only if it doesn't exist yet
|
|
9338
|
+
* */
|
|
9339
|
+
this.setPropertiesOnce = (nameOrProperties, value) => {
|
|
9340
|
+
let changed = false;
|
|
9341
|
+
if (isObject(nameOrProperties)) {
|
|
9342
|
+
Object.entries(nameOrProperties).forEach(([key, val]) => {
|
|
9343
|
+
if (!this.ownProperties[key] && !reservedProps.includes(key)) {
|
|
9344
|
+
this.ownProperties[key] = val;
|
|
9345
|
+
changed = true;
|
|
9346
|
+
}
|
|
9347
|
+
});
|
|
9348
|
+
}
|
|
9349
|
+
if (typeof nameOrProperties === 'string' && value !== undefined) {
|
|
9350
|
+
if (!this.ownProperties[nameOrProperties] && !reservedProps.includes(nameOrProperties)) {
|
|
9351
|
+
this.ownProperties[nameOrProperties] = value;
|
|
9352
|
+
changed = true;
|
|
9353
|
+
}
|
|
9354
|
+
}
|
|
9355
|
+
if (changed) {
|
|
9356
|
+
this.constantProperties.saveSuperProperties(this.ownProperties);
|
|
9357
|
+
}
|
|
9358
|
+
};
|
|
9359
|
+
/**
|
|
9360
|
+
* removes properties from list of super properties
|
|
9361
|
+
* */
|
|
9362
|
+
this.unsetProperties = (properties) => {
|
|
9363
|
+
let changed = false;
|
|
9364
|
+
if (Array.isArray(properties)) {
|
|
9365
|
+
properties.forEach((key) => {
|
|
9366
|
+
if (this.ownProperties[key] && !reservedProps.includes(key)) {
|
|
9367
|
+
delete this.ownProperties[key];
|
|
9368
|
+
changed = true;
|
|
9369
|
+
}
|
|
9370
|
+
});
|
|
9371
|
+
}
|
|
9372
|
+
else if (this.ownProperties[properties] && !reservedProps.includes(properties)) {
|
|
9373
|
+
delete this.ownProperties[properties];
|
|
9374
|
+
changed = true;
|
|
9375
|
+
}
|
|
9376
|
+
if (changed) {
|
|
9377
|
+
this.constantProperties.saveSuperProperties(this.ownProperties);
|
|
9378
|
+
}
|
|
9379
|
+
};
|
|
9380
|
+
/** clears all super properties */
|
|
9381
|
+
this.reset = () => {
|
|
9382
|
+
this.ownProperties = {};
|
|
9383
|
+
this.constantProperties.clearSuperProperties();
|
|
9384
|
+
};
|
|
9385
|
+
/** mixpanel compatibility */
|
|
9386
|
+
this.register = this.setProperty;
|
|
9387
|
+
this.register_once = this.setPropertiesOnce;
|
|
9388
|
+
this.unregister = this.unsetProperties;
|
|
9389
|
+
this.track = this.sendEvent;
|
|
9390
|
+
this.ownProperties = this.constantProperties.getSuperProperties();
|
|
9391
|
+
}
|
|
9392
|
+
}
|
|
9393
|
+
|
|
9394
|
+
class People {
|
|
9395
|
+
constructor(constantProperties, getTimestamp, onId, batcher) {
|
|
9396
|
+
this.constantProperties = constantProperties;
|
|
9397
|
+
this.getTimestamp = getTimestamp;
|
|
9398
|
+
this.onId = onId;
|
|
9399
|
+
this.batcher = batcher;
|
|
9400
|
+
this.ownProperties = {};
|
|
9401
|
+
this.identify = (user_id, options) => {
|
|
9402
|
+
if (!user_id || typeof user_id !== 'string') {
|
|
9403
|
+
throw new Error('OR SDK: user_id (string) is required for .identify()');
|
|
9404
|
+
}
|
|
9405
|
+
// if user exists already, reset properties
|
|
9406
|
+
if (this.constantProperties.user_id && this.constantProperties.user_id !== user_id) {
|
|
9407
|
+
this.reset();
|
|
9408
|
+
}
|
|
9409
|
+
this.constantProperties.setUserId(user_id);
|
|
9410
|
+
if (!options?.fromTracker) {
|
|
9411
|
+
this.onId(user_id);
|
|
9412
|
+
}
|
|
9413
|
+
const identityEvent = createEvent(categories.people, mutationTypes.identity, this.getTimestamp(), { user_id });
|
|
9414
|
+
this.batcher.addEvent(identityEvent);
|
|
9415
|
+
};
|
|
9416
|
+
/** Resets user id and own properties
|
|
9417
|
+
*
|
|
9418
|
+
* !hard reset will destroy persistent device id!
|
|
9419
|
+
* */
|
|
9420
|
+
this.reset = (hard) => {
|
|
9421
|
+
this.constantProperties.resetUserId(hard);
|
|
9422
|
+
this.ownProperties = {};
|
|
9423
|
+
};
|
|
9424
|
+
/**
|
|
9425
|
+
* Will delete user and its data from backend, then reset all local properties
|
|
9426
|
+
*/
|
|
9427
|
+
this.deleteUser = () => {
|
|
9428
|
+
const removedUser = this.constantProperties.user_id;
|
|
9429
|
+
if (!removedUser)
|
|
9430
|
+
return;
|
|
9431
|
+
this.constantProperties.setUserId(null);
|
|
9432
|
+
this.ownProperties = {};
|
|
9433
|
+
const deleteEvent = createEvent(categories.people, mutationTypes.deleteUser, undefined, {
|
|
9434
|
+
user_id: removedUser,
|
|
9435
|
+
});
|
|
9436
|
+
this.batcher.addEvent(deleteEvent);
|
|
9437
|
+
this.reset();
|
|
9438
|
+
};
|
|
9439
|
+
/**
|
|
9440
|
+
* set user properties, overwriting existing ones
|
|
9441
|
+
* */
|
|
9442
|
+
this.setProperties = (propertyOrObj, value) => {
|
|
9443
|
+
if (!propertyOrObj) {
|
|
9444
|
+
throw new Error('OR SDK: no user properties provided to set');
|
|
9445
|
+
}
|
|
9446
|
+
const properties = {};
|
|
9447
|
+
if (typeof propertyOrObj === 'string' && propertyOrObj && value) {
|
|
9448
|
+
properties[propertyOrObj] = value;
|
|
9449
|
+
}
|
|
9450
|
+
else if (isObject(propertyOrObj)) {
|
|
9451
|
+
Object.assign(properties, propertyOrObj);
|
|
9452
|
+
}
|
|
9453
|
+
else {
|
|
9454
|
+
throw new Error('OR SDK: invalid user properties provided to set');
|
|
9455
|
+
}
|
|
9456
|
+
Object.entries(properties).forEach(([key, value]) => {
|
|
9457
|
+
if (!this.constantProperties.defaultPropertyKeys.includes(key)) {
|
|
9458
|
+
this.ownProperties[key] = value;
|
|
9459
|
+
}
|
|
9460
|
+
});
|
|
9461
|
+
const setEvent = createEvent(categories.people, mutationTypes.setProperty, undefined, {
|
|
9462
|
+
user_id: this.user_id,
|
|
9463
|
+
properties,
|
|
9464
|
+
});
|
|
9465
|
+
this.batcher.addEvent(setEvent);
|
|
9466
|
+
};
|
|
9467
|
+
/**
|
|
9468
|
+
* Set property if it doesn't exist yet
|
|
9469
|
+
* */
|
|
9470
|
+
this.setPropertiesOnce = (properties) => {
|
|
9471
|
+
if (!isObject(properties)) {
|
|
9472
|
+
throw new Error('Properties must be an object');
|
|
9473
|
+
}
|
|
9474
|
+
Object.entries(properties).forEach(([key, value]) => {
|
|
9475
|
+
if (!this.constantProperties.defaultPropertyKeys.includes(key) && !this.ownProperties[key]) {
|
|
9476
|
+
this.ownProperties[key] = value;
|
|
9477
|
+
}
|
|
9478
|
+
});
|
|
9479
|
+
const setEvent = createEvent(categories.people, mutationTypes.setPropertyOnce, undefined, {
|
|
9480
|
+
user_id: this.user_id,
|
|
9481
|
+
properties,
|
|
9482
|
+
});
|
|
9483
|
+
this.batcher.addEvent(setEvent);
|
|
9484
|
+
};
|
|
9485
|
+
/**
|
|
9486
|
+
* Add value to property (will turn string prop into array)
|
|
9487
|
+
* */
|
|
9488
|
+
this.appendValues = (key, value) => {
|
|
9489
|
+
if (!this.constantProperties.defaultPropertyKeys.includes(key) && this.ownProperties[key]) {
|
|
9490
|
+
if (Array.isArray(this.ownProperties[key])) {
|
|
9491
|
+
this.ownProperties[key].push(value);
|
|
9492
|
+
}
|
|
9493
|
+
else {
|
|
9494
|
+
this.ownProperties[key] = [this.ownProperties[key], value];
|
|
9495
|
+
}
|
|
9496
|
+
}
|
|
9497
|
+
const appendEvent = createEvent(categories.people, mutationTypes.appendProperty, undefined, {
|
|
9498
|
+
properties: { [key]: value },
|
|
9499
|
+
user_id: this.user_id,
|
|
9500
|
+
});
|
|
9501
|
+
this.batcher.addEvent(appendEvent);
|
|
9502
|
+
};
|
|
9503
|
+
/**
|
|
9504
|
+
* Add unique values to property (will turn string prop into array)
|
|
9505
|
+
* */
|
|
9506
|
+
this.appendUniqueValues = (key, value) => {
|
|
9507
|
+
if (!this.ownProperties[key])
|
|
9508
|
+
return;
|
|
9509
|
+
if (Array.isArray(this.ownProperties[key])) {
|
|
9510
|
+
if (!this.ownProperties[key].includes(value)) {
|
|
9511
|
+
this.appendValues(key, value);
|
|
9512
|
+
}
|
|
9513
|
+
}
|
|
9514
|
+
else if (this.ownProperties[key] !== value) {
|
|
9515
|
+
this.appendValues(key, value);
|
|
9516
|
+
}
|
|
9517
|
+
const unionEvent = createEvent(categories.people, mutationTypes.appendUniqueProperty, undefined, {
|
|
9518
|
+
properties: { [key]: value },
|
|
9519
|
+
user_id: this.user_id,
|
|
9520
|
+
});
|
|
9521
|
+
this.batcher.addEvent(unionEvent);
|
|
9522
|
+
};
|
|
9523
|
+
/**
|
|
9524
|
+
* Adds value (incl. negative) to existing numerical property
|
|
9525
|
+
* */
|
|
9526
|
+
this.increment = (key, value) => {
|
|
9527
|
+
if (!this.ownProperties[key]) {
|
|
9528
|
+
this.ownProperties[key] = 0;
|
|
9529
|
+
}
|
|
9530
|
+
if (this.ownProperties[key] && typeof this.ownProperties[key] !== 'number') {
|
|
9531
|
+
throw new Error('OR SDK: Property must be a number to increment');
|
|
9532
|
+
}
|
|
9533
|
+
// @ts-ignore
|
|
9534
|
+
this.ownProperties[key] += value;
|
|
9535
|
+
const incrementEvent = createEvent(categories.people, mutationTypes.incrementProperty, undefined, {
|
|
9536
|
+
user_id: this.user_id,
|
|
9537
|
+
properties: { [key]: value },
|
|
9538
|
+
});
|
|
9539
|
+
this.batcher.addEvent(incrementEvent);
|
|
9540
|
+
};
|
|
9541
|
+
/** mixpanel compatibility */
|
|
9542
|
+
this.union = this.appendUniqueValues;
|
|
9543
|
+
this.set = this.setProperties;
|
|
9544
|
+
this.set_once = this.setPropertiesOnce;
|
|
9545
|
+
this.append = this.appendValues;
|
|
9546
|
+
this.incrementBy = this.increment;
|
|
9547
|
+
}
|
|
9548
|
+
get user_id() {
|
|
9549
|
+
return this.constantProperties.user_id;
|
|
9550
|
+
}
|
|
9551
|
+
}
|
|
9552
|
+
|
|
9553
|
+
/**
|
|
9554
|
+
* Creates batches of events, then sends them at intervals.
|
|
9555
|
+
*/
|
|
9556
|
+
class Batcher {
|
|
9557
|
+
constructor(backendUrl, getToken, init) {
|
|
9558
|
+
this.backendUrl = backendUrl;
|
|
9559
|
+
this.getToken = getToken;
|
|
9560
|
+
this.init = init;
|
|
9561
|
+
this.autosendInterval = 5 * 1000;
|
|
9562
|
+
this.retryTimeout = 3 * 1000;
|
|
9563
|
+
this.retryLimit = 3;
|
|
9564
|
+
this.apiEdp = '/v1/sdk/i';
|
|
9565
|
+
this.batch = {
|
|
9566
|
+
[categories.people]: [],
|
|
9567
|
+
[categories.events]: [],
|
|
9568
|
+
};
|
|
9569
|
+
this.intervalId = null;
|
|
9570
|
+
}
|
|
9571
|
+
getBatches() {
|
|
9572
|
+
this.batch[categories.people] = this.dedupePeopleEvents();
|
|
9573
|
+
const finalData = { data: this.batch };
|
|
9574
|
+
return finalData;
|
|
9575
|
+
}
|
|
9576
|
+
addEvent(event) {
|
|
9577
|
+
this.batch[event.category].push(event.data);
|
|
9578
|
+
}
|
|
9579
|
+
sendImmediately(event) {
|
|
9580
|
+
this.sendBatch({ [event.category]: [event.data] });
|
|
9581
|
+
}
|
|
9582
|
+
/**
|
|
9583
|
+
*
|
|
9584
|
+
* Essentially we're dividing the batch by identify events and squash all same category events into one in each part,
|
|
9585
|
+
* taking priority to the last one
|
|
9586
|
+
*/
|
|
9587
|
+
dedupePeopleEvents() {
|
|
9588
|
+
const peopleEvents = this.batch[categories.people];
|
|
9589
|
+
const finalEvents = [];
|
|
9590
|
+
const currentPart = [];
|
|
9591
|
+
for (let event of peopleEvents) {
|
|
9592
|
+
if (event.type === 'identity') {
|
|
9593
|
+
if (currentPart.length > 0) {
|
|
9594
|
+
finalEvents.push(...this.squashPeopleEvents(currentPart), event);
|
|
9595
|
+
currentPart.length = 0;
|
|
9596
|
+
}
|
|
9597
|
+
else {
|
|
9598
|
+
finalEvents.push(event);
|
|
9599
|
+
}
|
|
9600
|
+
}
|
|
9601
|
+
else {
|
|
9602
|
+
currentPart.push(event);
|
|
9603
|
+
}
|
|
9604
|
+
}
|
|
9605
|
+
if (currentPart.length > 0) {
|
|
9606
|
+
finalEvents.push(...this.squashPeopleEvents(currentPart));
|
|
9607
|
+
}
|
|
9608
|
+
return finalEvents;
|
|
9609
|
+
}
|
|
9610
|
+
squashPeopleEvents(events) {
|
|
9611
|
+
if (!events || events.length === 0) {
|
|
9612
|
+
return [];
|
|
9613
|
+
}
|
|
9614
|
+
const uniqueEventsByType = new Map();
|
|
9615
|
+
for (let event of events) {
|
|
9616
|
+
const prev = uniqueEventsByType.get(event.type);
|
|
9617
|
+
if (prev) {
|
|
9618
|
+
if (event.type === 'increment_property') {
|
|
9619
|
+
const previousValues = Object.entries(prev.payload);
|
|
9620
|
+
const currentValues = Object.entries(event.payload);
|
|
9621
|
+
const uniqueKeys = new Set([...previousValues.map(([key]) => key), ...currentValues.map(([key]) => key)]);
|
|
9622
|
+
const mergedPayload = {};
|
|
9623
|
+
uniqueKeys.forEach((key) => {
|
|
9624
|
+
const prevValue = typeof prev.payload[key] === 'number' ? prev.payload[key] : 0;
|
|
9625
|
+
const currValue = typeof event.payload[key] === 'number' ? event.payload[key] : 0;
|
|
9626
|
+
mergedPayload[key] = prevValue + currValue;
|
|
9627
|
+
});
|
|
9628
|
+
uniqueEventsByType.set(event.type, {
|
|
9629
|
+
type: event.type,
|
|
9630
|
+
timestamp: event.timestamp,
|
|
9631
|
+
payload: mergedPayload,
|
|
9632
|
+
});
|
|
9633
|
+
continue;
|
|
9634
|
+
}
|
|
9635
|
+
// merge payloads, taking priority to the latest one
|
|
9636
|
+
uniqueEventsByType.set(event.type, {
|
|
9637
|
+
type: event.type,
|
|
9638
|
+
timestamp: event.timestamp,
|
|
9639
|
+
payload: { ...(prev.payload ?? {}), ...(event.payload ?? {}) },
|
|
9640
|
+
});
|
|
9641
|
+
}
|
|
9642
|
+
else {
|
|
9643
|
+
uniqueEventsByType.set(event.type, event);
|
|
9644
|
+
}
|
|
9645
|
+
}
|
|
9646
|
+
return Array.from(uniqueEventsByType.values());
|
|
9647
|
+
}
|
|
9648
|
+
sendBatch(batch) {
|
|
9649
|
+
const sentBatch = batch;
|
|
9650
|
+
let attempts = 0;
|
|
9651
|
+
const send = () => {
|
|
9652
|
+
const token = this.getToken();
|
|
9653
|
+
if (!token) {
|
|
9654
|
+
return;
|
|
9655
|
+
}
|
|
9656
|
+
attempts++;
|
|
9657
|
+
return fetch(`${this.backendUrl}${this.apiEdp}`, {
|
|
9658
|
+
method: 'POST',
|
|
9659
|
+
headers: {
|
|
9660
|
+
'Content-Type': 'application/json',
|
|
9661
|
+
Authorization: `Bearer ${token}`,
|
|
9662
|
+
},
|
|
9663
|
+
body: JSON.stringify(sentBatch),
|
|
9664
|
+
})
|
|
9665
|
+
.then((response) => {
|
|
9666
|
+
if (response.status === 403) {
|
|
9667
|
+
this.init().then(() => {
|
|
9668
|
+
send();
|
|
9669
|
+
});
|
|
9670
|
+
}
|
|
9671
|
+
if (!response.ok) {
|
|
9672
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
9673
|
+
}
|
|
9674
|
+
})
|
|
9675
|
+
.catch(() => {
|
|
9676
|
+
if (attempts < this.retryLimit) {
|
|
9677
|
+
setTimeout(() => void send(), this.retryTimeout);
|
|
9678
|
+
}
|
|
9679
|
+
});
|
|
9680
|
+
};
|
|
9681
|
+
void send();
|
|
9682
|
+
}
|
|
9683
|
+
startAutosend() {
|
|
9684
|
+
this.intervalId = setInterval(() => {
|
|
9685
|
+
this.flush();
|
|
9686
|
+
}, this.autosendInterval);
|
|
9687
|
+
}
|
|
9688
|
+
flush() {
|
|
9689
|
+
const categories = Object.keys(this.batch);
|
|
9690
|
+
const isEmpty = categories.every((category) => this.batch[category].length === 0);
|
|
9691
|
+
if (isEmpty) {
|
|
9692
|
+
return;
|
|
9693
|
+
}
|
|
9694
|
+
this.sendBatch(this.getBatches());
|
|
9695
|
+
categories.forEach((key) => {
|
|
9696
|
+
this.batch[key] = [];
|
|
9697
|
+
});
|
|
9698
|
+
}
|
|
9699
|
+
stop() {
|
|
9700
|
+
this.flush();
|
|
9701
|
+
if (this.intervalId) {
|
|
9702
|
+
clearInterval(this.intervalId);
|
|
9703
|
+
this.intervalId = null;
|
|
9704
|
+
}
|
|
9705
|
+
}
|
|
9706
|
+
}
|
|
9707
|
+
|
|
9708
|
+
const STORAGEKEY = '__or_sdk_analytics_token';
|
|
9709
|
+
class Analytics {
|
|
9710
|
+
/**
|
|
9711
|
+
* @param localStorage Class or Object that implements Storage-like interface that stores
|
|
9712
|
+
* values persistently like window.localStorage or any other file-based storage
|
|
9713
|
+
*
|
|
9714
|
+
* @param sessionStorage Class or Object that implements Storage-like interface that stores values
|
|
9715
|
+
* on per-session basis like window.sessionStorage or any other in-memory storage
|
|
9716
|
+
*
|
|
9717
|
+
* @param getToken Function that returns token to bind events to a session
|
|
9718
|
+
*
|
|
9719
|
+
* @param getTimestamp returns current timestamp
|
|
9720
|
+
*
|
|
9721
|
+
* @param setUserId callback for people.identify
|
|
9722
|
+
*
|
|
9723
|
+
* @param standalone if true, analytics will manage its own token (instead of using with openreplay tracker session)
|
|
9724
|
+
* */
|
|
9725
|
+
constructor(options) {
|
|
9726
|
+
this.token = null;
|
|
9727
|
+
this.standalone = false;
|
|
9728
|
+
this._getToken = () => {
|
|
9729
|
+
if (this.standalone) {
|
|
9730
|
+
return this.token;
|
|
9731
|
+
}
|
|
9732
|
+
return this.getToken();
|
|
9733
|
+
};
|
|
9734
|
+
this._getTimestamp = () => {
|
|
9735
|
+
if (this.standalone) {
|
|
9736
|
+
return Date.now();
|
|
9737
|
+
}
|
|
9738
|
+
return this.getTimestamp();
|
|
9739
|
+
};
|
|
9740
|
+
this.init = async () => {
|
|
9741
|
+
if (!this.standalone) {
|
|
9742
|
+
this.batcher.startAutosend();
|
|
9743
|
+
return;
|
|
9744
|
+
}
|
|
9745
|
+
else {
|
|
9746
|
+
const defaultFields = this.constantProperties.all;
|
|
9747
|
+
const apiEdp = '/v1/sdk/start';
|
|
9748
|
+
const data = {
|
|
9749
|
+
projectKey: this.projectKey,
|
|
9750
|
+
defaultFields,
|
|
9751
|
+
};
|
|
9752
|
+
const resp = await fetch(apiEdp, {
|
|
9753
|
+
method: 'POST',
|
|
9754
|
+
body: JSON.stringify(data),
|
|
9755
|
+
});
|
|
9756
|
+
if (!resp.ok) {
|
|
9757
|
+
throw new Error(`HTTP error! status: ${resp.status}`);
|
|
9758
|
+
}
|
|
9759
|
+
const result = await resp.json();
|
|
9760
|
+
if (result.token) {
|
|
9761
|
+
this.token = result.token;
|
|
9762
|
+
this.sessionStorage.setItem(STORAGEKEY, result.token);
|
|
9763
|
+
}
|
|
9764
|
+
else {
|
|
9765
|
+
throw new Error('No token received from server');
|
|
9766
|
+
}
|
|
9767
|
+
}
|
|
9768
|
+
};
|
|
9769
|
+
this.reset = () => {
|
|
9770
|
+
this.people.reset(true);
|
|
9771
|
+
this.events.reset();
|
|
9772
|
+
this.batcher.stop();
|
|
9773
|
+
if (this.standalone) {
|
|
9774
|
+
this.token = null;
|
|
9775
|
+
this.sessionStorage.setItem(STORAGEKEY, '');
|
|
9776
|
+
}
|
|
9777
|
+
};
|
|
9778
|
+
/**
|
|
9779
|
+
* COMPATIBILITY LAYER
|
|
9780
|
+
* */
|
|
9781
|
+
/**
|
|
9782
|
+
* Identify a user with an id (e.g. email, username, etc.)
|
|
9783
|
+
* will bind all events and properties (including device_id) to this user
|
|
9784
|
+
*
|
|
9785
|
+
* you will need to manually call people.reset() to clear the id on logout event
|
|
9786
|
+
* */
|
|
9787
|
+
this.identify = (user_id) => {
|
|
9788
|
+
return this.people.identify(user_id);
|
|
9789
|
+
};
|
|
9790
|
+
/**
|
|
9791
|
+
* Add event to batch with option to send it immediately,
|
|
9792
|
+
* properties are optional and will not be saved as super prop
|
|
9793
|
+
* */
|
|
9794
|
+
this.track = (eventName, properties, options) => {
|
|
9795
|
+
return this.events.track(eventName, properties, options);
|
|
9796
|
+
};
|
|
9797
|
+
this.sessionStorage = options.sessionStorage || sessionStorage;
|
|
9798
|
+
this.localStorage = options.localStorage || localStorage;
|
|
9799
|
+
this.backendUrl = options.ingestPoint;
|
|
9800
|
+
this.projectKey = options.projectKey;
|
|
9801
|
+
this.getToken = options.getToken || (() => '');
|
|
9802
|
+
this.getTimestamp = options.getTimestamp || (() => Date.now());
|
|
9803
|
+
this.setUserId = options.setUserId || (() => { });
|
|
9804
|
+
this.standalone = !options.notStandalone;
|
|
9805
|
+
this.token = this.sessionStorage.getItem(STORAGEKEY);
|
|
9806
|
+
this.constantProperties = new ConstantProperties(this.localStorage, this.sessionStorage);
|
|
9807
|
+
this.batcher = new Batcher(this.backendUrl, this._getToken, this.init);
|
|
9808
|
+
this.events = new Events(this.constantProperties, this._getTimestamp, this.batcher);
|
|
9809
|
+
this.people = new People(this.constantProperties, this._getTimestamp, this.setUserId, this.batcher);
|
|
9810
|
+
if (options.notStandalone) {
|
|
9811
|
+
this.init();
|
|
9812
|
+
}
|
|
9813
|
+
}
|
|
9814
|
+
}
|
|
9815
|
+
|
|
8819
9816
|
const Messages = _Messages;
|
|
8820
9817
|
const DOCS_SETUP = '/en/sdk';
|
|
8821
9818
|
function processOptions(obj) {
|
|
@@ -8857,6 +9854,7 @@ class API {
|
|
|
8857
9854
|
constructor(options) {
|
|
8858
9855
|
this.options = options;
|
|
8859
9856
|
this.app = null;
|
|
9857
|
+
this.analytics = null;
|
|
8860
9858
|
this.crossdomainMode = false;
|
|
8861
9859
|
this.checkDoNotTrack = () => {
|
|
8862
9860
|
return (this.options.respectDoNotTrack &&
|
|
@@ -8867,7 +9865,7 @@ class API {
|
|
|
8867
9865
|
this.signalStartIssue = (reason, missingApi) => {
|
|
8868
9866
|
const doNotTrack = this.checkDoNotTrack();
|
|
8869
9867
|
console.log("Tracker couldn't start due to:", JSON.stringify({
|
|
8870
|
-
trackerVersion: '17.0
|
|
9868
|
+
trackerVersion: '17.1.0',
|
|
8871
9869
|
projectKey: this.options.projectKey,
|
|
8872
9870
|
doNotTrack,
|
|
8873
9871
|
reason: missingApi.length ? `missing api: ${missingApi.join(',')}` : reason,
|
|
@@ -8879,6 +9877,22 @@ class API {
|
|
|
8879
9877
|
}
|
|
8880
9878
|
this.app.restartCanvasTracking();
|
|
8881
9879
|
};
|
|
9880
|
+
this.getSessionURL = (options) => {
|
|
9881
|
+
if (this.app === null) {
|
|
9882
|
+
return undefined;
|
|
9883
|
+
}
|
|
9884
|
+
return this.app.getSessionURL(options);
|
|
9885
|
+
};
|
|
9886
|
+
this.setUserID = (id) => {
|
|
9887
|
+
if (typeof id === 'string' && this.app !== null) {
|
|
9888
|
+
this.app.session.setUserID(id);
|
|
9889
|
+
this.analytics?.people.identify(id, { fromTracker: true });
|
|
9890
|
+
}
|
|
9891
|
+
};
|
|
9892
|
+
this.userID = (id) => {
|
|
9893
|
+
deprecationWarn("'userID' method", "'setUserID' method", '/');
|
|
9894
|
+
this.setUserID(id);
|
|
9895
|
+
};
|
|
8882
9896
|
this.handleError = (e, metadata = {}) => {
|
|
8883
9897
|
if (this.app === null) {
|
|
8884
9898
|
return;
|
|
@@ -8901,6 +9915,21 @@ class API {
|
|
|
8901
9915
|
}
|
|
8902
9916
|
this.app.send(Incident(options.label ?? '', options.startTime, options.endTime ?? options.startTime));
|
|
8903
9917
|
};
|
|
9918
|
+
this.analyticsToken = null;
|
|
9919
|
+
/**
|
|
9920
|
+
* Use custom token for analytics events without session recording
|
|
9921
|
+
* */
|
|
9922
|
+
this.setAnalyticsToken = (token) => {
|
|
9923
|
+
this.analyticsToken = token;
|
|
9924
|
+
};
|
|
9925
|
+
this.getAnalyticsToken = () => {
|
|
9926
|
+
if (this.analyticsToken) {
|
|
9927
|
+
return this.analyticsToken;
|
|
9928
|
+
}
|
|
9929
|
+
else {
|
|
9930
|
+
return this.app?.session.getSessionToken() ?? '';
|
|
9931
|
+
}
|
|
9932
|
+
};
|
|
8904
9933
|
this.crossdomainMode = Boolean(inIframe() && options.crossdomain?.enabled);
|
|
8905
9934
|
if (!IN_BROWSER || !processOptions(options)) {
|
|
8906
9935
|
return;
|
|
@@ -8959,6 +9988,24 @@ class API {
|
|
|
8959
9988
|
}
|
|
8960
9989
|
const app = new App(options.projectKey, options.sessionToken, options, this.signalStartIssue, this.crossdomainMode);
|
|
8961
9990
|
this.app = app;
|
|
9991
|
+
if (options.projectKey && options.analytics?.active) {
|
|
9992
|
+
const isSaas = !options.ingestPoint || options.ingestPoint.includes('api.openreplay.com');
|
|
9993
|
+
const defaultEdp = 'https://api.openreplay.com/ingest';
|
|
9994
|
+
this.analytics = new Analytics({
|
|
9995
|
+
localStorage: options.localStorage ?? localStorage,
|
|
9996
|
+
sessionStorage: options.sessionStorage ?? sessionStorage,
|
|
9997
|
+
getToken: () => this.getAnalyticsToken(),
|
|
9998
|
+
getTimestamp: () => this.app?.timestamp() ?? Date.now(),
|
|
9999
|
+
setUserId: (id) => {
|
|
10000
|
+
this.app?.session.setUserID(id);
|
|
10001
|
+
},
|
|
10002
|
+
notStandalone: true,
|
|
10003
|
+
ingestPoint: isSaas
|
|
10004
|
+
? defaultEdp
|
|
10005
|
+
: (options.analytics?.ingestPoint ?? options.ingestPoint ?? defaultEdp),
|
|
10006
|
+
projectKey: options.projectKey,
|
|
10007
|
+
});
|
|
10008
|
+
}
|
|
8962
10009
|
if (!this.crossdomainMode) {
|
|
8963
10010
|
// no need to send iframe viewport data since its a node for us
|
|
8964
10011
|
Viewport(app, options.urls);
|
|
@@ -9038,6 +10085,9 @@ class API {
|
|
|
9038
10085
|
if (this.app === null) {
|
|
9039
10086
|
return Promise.reject("Browser doesn't support required api, or doNotTrack is active.");
|
|
9040
10087
|
}
|
|
10088
|
+
if (startOpts?.userID) {
|
|
10089
|
+
this.analytics?.people.identify(startOpts.userID, { fromTracker: true });
|
|
10090
|
+
}
|
|
9041
10091
|
return this.app.start(startOpts);
|
|
9042
10092
|
}
|
|
9043
10093
|
else {
|
|
@@ -9145,21 +10195,6 @@ class API {
|
|
|
9145
10195
|
deprecationWarn("'sessionID' method", "'getSessionID' method", '/');
|
|
9146
10196
|
return this.getSessionID();
|
|
9147
10197
|
}
|
|
9148
|
-
getSessionURL(options) {
|
|
9149
|
-
if (this.app === null) {
|
|
9150
|
-
return undefined;
|
|
9151
|
-
}
|
|
9152
|
-
return this.app.getSessionURL(options);
|
|
9153
|
-
}
|
|
9154
|
-
setUserID(id) {
|
|
9155
|
-
if (typeof id === 'string' && this.app !== null) {
|
|
9156
|
-
this.app.session.setUserID(id);
|
|
9157
|
-
}
|
|
9158
|
-
}
|
|
9159
|
-
userID(id) {
|
|
9160
|
-
deprecationWarn("'userID' method", "'setUserID' method", '/');
|
|
9161
|
-
this.setUserID(id);
|
|
9162
|
-
}
|
|
9163
10198
|
setUserAnonymousID(id) {
|
|
9164
10199
|
if (typeof id === 'string' && this.app !== null) {
|
|
9165
10200
|
this.app.send(UserAnonymousID(id));
|
|
@@ -9446,8 +10481,10 @@ class TrackerSingleton {
|
|
|
9446
10481
|
}
|
|
9447
10482
|
const tracker = new TrackerSingleton();
|
|
9448
10483
|
|
|
10484
|
+
exports.Analytics = Analytics;
|
|
9449
10485
|
exports.App = App;
|
|
9450
10486
|
exports.Messages = Messages;
|
|
9451
10487
|
exports.default = API;
|
|
10488
|
+
exports.openReplay = tracker;
|
|
9452
10489
|
exports.tracker = tracker;
|
|
9453
10490
|
//# sourceMappingURL=entry.js.map
|