@openreplay/tracker 17.0.1 → 17.1.1

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