@openreplay/tracker 16.4.2 → 16.4.3

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.
@@ -252,7 +252,7 @@ export default class App {
252
252
  * user 30 seconds to "activate" and record session by calling `start()` on conditional trigger,
253
253
  * and we will then send buffered batch, so it won't get lost
254
254
  * */
255
- coldStart(startOpts?: StartOptions, conditional?: boolean): Promise<void>;
255
+ coldStart: (startOpts?: StartOptions, conditional?: boolean) => Promise<void>;
256
256
  private setupConditionalStart;
257
257
  onSessionSent: () => void;
258
258
  /**
@@ -260,7 +260,7 @@ export default class App {
260
260
  * @param {Object} startOpts - options for session start, same as .start()
261
261
  * @param {Function} onSessionSent - callback that will be called once session is fully sent
262
262
  * */
263
- offlineRecording(startOpts: StartOptions | undefined, onSessionSent: () => void): {
263
+ offlineRecording: (startOpts: StartOptions | undefined, onSessionSent: () => void) => {
264
264
  saveBuffer: () => void;
265
265
  getBuffer: () => Message[];
266
266
  setBuffer: (buffer: Message[]) => void;
@@ -272,15 +272,15 @@ export default class App {
272
272
  *
273
273
  * Keeping the size of local storage reasonable is up to the end users of this library
274
274
  * */
275
- saveBuffer(): void;
275
+ saveBuffer: () => void;
276
276
  /**
277
277
  * @returns buffer with stored messages for offline recording
278
278
  * */
279
- getBuffer(): Message[];
279
+ getBuffer: () => Message[];
280
280
  /**
281
281
  * Used to set a buffer with messages array
282
282
  * */
283
- setBuffer(buffer: Message[]): void;
283
+ setBuffer: (buffer: Message[]) => void;
284
284
  /**
285
285
  * Uploads the stored session buffer to backend
286
286
  * @returns promise that resolves once messages are loaded, it has to be awaited
package/dist/lib/entry.js CHANGED
@@ -5094,15 +5094,16 @@ class Session {
5094
5094
  this.userInfo = userInfo;
5095
5095
  }
5096
5096
  getSessionToken(projectKey) {
5097
- const token = this.token || this.app.sessionStorage.getItem(this.options.session_token_key);
5098
- if (projectKey && token) {
5099
- const savedProject = token.split('_&_')[1];
5097
+ const tokenWithProject = this.token || this.app.sessionStorage.getItem(this.options.session_token_key);
5098
+ if (projectKey && tokenWithProject) {
5099
+ const savedProject = tokenWithProject.split('_&_')[1];
5100
5100
  if (!savedProject || savedProject !== projectKey) {
5101
5101
  this.app.sessionStorage.removeItem(this.options.session_token_key);
5102
5102
  this.token = undefined;
5103
5103
  return undefined;
5104
5104
  }
5105
5105
  }
5106
+ const token = tokenWithProject ? tokenWithProject.split('_&_')[0] : null;
5106
5107
  return token || undefined;
5107
5108
  }
5108
5109
  setSessionToken(token, projectKey) {
@@ -5271,7 +5272,7 @@ class App {
5271
5272
  this.stopCallbacks = [];
5272
5273
  this.commitCallbacks = [];
5273
5274
  this.activityState = ActivityState.NotActive;
5274
- this.version = '16.4.2'; // TODO: version compatability check inside each plugin.
5275
+ this.version = '16.4.3'; // TODO: version compatability check inside each plugin.
5275
5276
  this.socketMode = false;
5276
5277
  this.compressionThreshold = 24 * 1000;
5277
5278
  this.bc = null;
@@ -5559,9 +5560,170 @@ class App {
5559
5560
  this.orderNumber = 0;
5560
5561
  this.coldStartTs = 0;
5561
5562
  this.singleBuffer = false;
5563
+ /**
5564
+ * start buffering messages without starting the actual session, which gives
5565
+ * user 30 seconds to "activate" and record session by calling `start()` on conditional trigger,
5566
+ * and we will then send buffered batch, so it won't get lost
5567
+ * */
5568
+ this.coldStart = async (startOpts = {}, conditional) => {
5569
+ this.singleBuffer = false;
5570
+ const second = 1000;
5571
+ const isNewSession = this.checkSessionToken(startOpts.forceNew);
5572
+ if (conditional) {
5573
+ await this.setupConditionalStart(startOpts);
5574
+ }
5575
+ const cycle = () => {
5576
+ this.orderNumber += 1;
5577
+ adjustTimeOrigin();
5578
+ this.coldStartTs = now();
5579
+ if (this.orderNumber % 2 === 0) {
5580
+ this.bufferedMessages1.length = 0;
5581
+ this.bufferedMessages1.push(Timestamp(this.timestamp()));
5582
+ this.bufferedMessages1.push(TabData(this.session.getTabId()));
5583
+ }
5584
+ else {
5585
+ this.bufferedMessages2.length = 0;
5586
+ this.bufferedMessages2.push(Timestamp(this.timestamp()));
5587
+ this.bufferedMessages2.push(TabData(this.session.getTabId()));
5588
+ }
5589
+ this.stop(false);
5590
+ this.activityState = ActivityState.ColdStart;
5591
+ if (startOpts.sessionHash) {
5592
+ this.session.applySessionHash(startOpts.sessionHash);
5593
+ }
5594
+ if (startOpts.forceNew) {
5595
+ this.session.reset();
5596
+ }
5597
+ this.session.assign({
5598
+ userID: startOpts.userID,
5599
+ metadata: startOpts.metadata,
5600
+ });
5601
+ if (!isNewSession) {
5602
+ this.debug.log('continuing session on new tab', this.session.getTabId());
5603
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
5604
+ this.send(TabChange(this.session.getTabId()));
5605
+ }
5606
+ this.observer.observe();
5607
+ this.ticker.start();
5608
+ };
5609
+ this.coldInterval = setInterval(() => {
5610
+ cycle();
5611
+ }, 30 * second);
5612
+ cycle();
5613
+ };
5614
+ this.setupConditionalStart = async (startOpts) => {
5615
+ this.conditionsManager = new ConditionsManager(this, startOpts);
5616
+ const r = await fetch(this.options.ingestPoint + '/v1/web/start', {
5617
+ method: 'POST',
5618
+ headers: {
5619
+ 'Content-Type': 'application/json',
5620
+ },
5621
+ body: JSON.stringify({
5622
+ ...this.getTrackerInfo(),
5623
+ timestamp: now(),
5624
+ doNotRecord: true,
5625
+ bufferDiff: 0,
5626
+ userID: this.session.getInfo().userID,
5627
+ token: undefined,
5628
+ deviceMemory,
5629
+ jsHeapSizeLimit,
5630
+ timezone: getTimezone(),
5631
+ width: window.screen.width,
5632
+ height: window.screen.height,
5633
+ }),
5634
+ });
5635
+ const {
5636
+ // this token is needed to fetch conditions and flags,
5637
+ // but it can't be used to record a session
5638
+ token, userBrowser, userCity, userCountry, userDevice, userOS, userState, projectID, features, } = await r.json();
5639
+ this.features = features ? features : this.features;
5640
+ this.session.assign({ projectID });
5641
+ this.session.setUserInfo({
5642
+ userBrowser,
5643
+ userCity,
5644
+ userCountry,
5645
+ userDevice,
5646
+ userOS,
5647
+ userState,
5648
+ });
5649
+ const onStartInfo = { sessionToken: token, userUUID: '', sessionID: '' };
5650
+ this.startCallbacks.forEach((cb) => cb(onStartInfo));
5651
+ await this.conditionsManager?.fetchConditions(projectID, token);
5652
+ if (this.features['feature-flags']) {
5653
+ await this.featureFlags.reloadFlags(token);
5654
+ this.conditionsManager?.processFlags(this.featureFlags.flags);
5655
+ }
5656
+ await this.tagWatcher.fetchTags(this.options.ingestPoint, token);
5657
+ };
5562
5658
  this.onSessionSent = () => {
5563
5659
  return;
5564
5660
  };
5661
+ /**
5662
+ * Starts offline session recording
5663
+ * @param {Object} startOpts - options for session start, same as .start()
5664
+ * @param {Function} onSessionSent - callback that will be called once session is fully sent
5665
+ * */
5666
+ this.offlineRecording = (startOpts = {}, onSessionSent) => {
5667
+ this.onSessionSent = onSessionSent;
5668
+ this.singleBuffer = true;
5669
+ const isNewSession = this.checkSessionToken(startOpts.forceNew);
5670
+ adjustTimeOrigin();
5671
+ this.coldStartTs = now();
5672
+ const saverBuffer = this.localStorage.getItem(bufferStorageKey);
5673
+ if (saverBuffer) {
5674
+ const data = JSON.parse(saverBuffer);
5675
+ this.bufferedMessages1 = Array.isArray(data) ? data : this.bufferedMessages1;
5676
+ this.localStorage.removeItem(bufferStorageKey);
5677
+ }
5678
+ this.bufferedMessages1.push(Timestamp(this.timestamp()));
5679
+ this.bufferedMessages1.push(TabData(this.session.getTabId()));
5680
+ this.activityState = ActivityState.ColdStart;
5681
+ if (startOpts.sessionHash) {
5682
+ this.session.applySessionHash(startOpts.sessionHash);
5683
+ }
5684
+ if (startOpts.forceNew) {
5685
+ this.session.reset();
5686
+ }
5687
+ this.session.assign({
5688
+ userID: startOpts.userID,
5689
+ metadata: startOpts.metadata,
5690
+ });
5691
+ const onStartInfo = { sessionToken: '', userUUID: '', sessionID: '' };
5692
+ this.startCallbacks.forEach((cb) => cb(onStartInfo));
5693
+ if (!isNewSession) {
5694
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
5695
+ this.send(TabChange(this.session.getTabId()));
5696
+ }
5697
+ this.observer.observe();
5698
+ this.ticker.start();
5699
+ return {
5700
+ saveBuffer: this.saveBuffer,
5701
+ getBuffer: this.getBuffer,
5702
+ setBuffer: this.setBuffer,
5703
+ };
5704
+ };
5705
+ /**
5706
+ * Saves the captured messages in localStorage (or whatever is used in its place)
5707
+ *
5708
+ * Then, when this.offlineRecording is called, it will preload this messages and clear the storage item
5709
+ *
5710
+ * Keeping the size of local storage reasonable is up to the end users of this library
5711
+ * */
5712
+ this.saveBuffer = () => {
5713
+ this.localStorage.setItem(bufferStorageKey, JSON.stringify(this.bufferedMessages1));
5714
+ };
5715
+ /**
5716
+ * @returns buffer with stored messages for offline recording
5717
+ * */
5718
+ this.getBuffer = () => {
5719
+ return this.bufferedMessages1;
5720
+ };
5721
+ /**
5722
+ * Used to set a buffer with messages array
5723
+ * */
5724
+ this.setBuffer = (buffer) => {
5725
+ this.bufferedMessages1 = buffer;
5726
+ };
5565
5727
  this.prevOpts = {};
5566
5728
  this.restartCanvasTracking = () => {
5567
5729
  this.canvasRecorder?.restartTracking();
@@ -6068,167 +6230,6 @@ class App {
6068
6230
  const sessionToken = this.session.getSessionToken(this.projectKey);
6069
6231
  return needNewSessionID || !sessionToken;
6070
6232
  }
6071
- /**
6072
- * start buffering messages without starting the actual session, which gives
6073
- * user 30 seconds to "activate" and record session by calling `start()` on conditional trigger,
6074
- * and we will then send buffered batch, so it won't get lost
6075
- * */
6076
- async coldStart(startOpts = {}, conditional) {
6077
- this.singleBuffer = false;
6078
- const second = 1000;
6079
- const isNewSession = this.checkSessionToken(startOpts.forceNew);
6080
- if (conditional) {
6081
- await this.setupConditionalStart(startOpts);
6082
- }
6083
- const cycle = () => {
6084
- this.orderNumber += 1;
6085
- adjustTimeOrigin();
6086
- this.coldStartTs = now();
6087
- if (this.orderNumber % 2 === 0) {
6088
- this.bufferedMessages1.length = 0;
6089
- this.bufferedMessages1.push(Timestamp(this.timestamp()));
6090
- this.bufferedMessages1.push(TabData(this.session.getTabId()));
6091
- }
6092
- else {
6093
- this.bufferedMessages2.length = 0;
6094
- this.bufferedMessages2.push(Timestamp(this.timestamp()));
6095
- this.bufferedMessages2.push(TabData(this.session.getTabId()));
6096
- }
6097
- this.stop(false);
6098
- this.activityState = ActivityState.ColdStart;
6099
- if (startOpts.sessionHash) {
6100
- this.session.applySessionHash(startOpts.sessionHash);
6101
- }
6102
- if (startOpts.forceNew) {
6103
- this.session.reset();
6104
- }
6105
- this.session.assign({
6106
- userID: startOpts.userID,
6107
- metadata: startOpts.metadata,
6108
- });
6109
- if (!isNewSession) {
6110
- this.debug.log('continuing session on new tab', this.session.getTabId());
6111
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
6112
- this.send(TabChange(this.session.getTabId()));
6113
- }
6114
- this.observer.observe();
6115
- this.ticker.start();
6116
- };
6117
- this.coldInterval = setInterval(() => {
6118
- cycle();
6119
- }, 30 * second);
6120
- cycle();
6121
- }
6122
- async setupConditionalStart(startOpts) {
6123
- this.conditionsManager = new ConditionsManager(this, startOpts);
6124
- const r = await fetch(this.options.ingestPoint + '/v1/web/start', {
6125
- method: 'POST',
6126
- headers: {
6127
- 'Content-Type': 'application/json',
6128
- },
6129
- body: JSON.stringify({
6130
- ...this.getTrackerInfo(),
6131
- timestamp: now(),
6132
- doNotRecord: true,
6133
- bufferDiff: 0,
6134
- userID: this.session.getInfo().userID,
6135
- token: undefined,
6136
- deviceMemory,
6137
- jsHeapSizeLimit,
6138
- timezone: getTimezone(),
6139
- width: window.screen.width,
6140
- height: window.screen.height,
6141
- }),
6142
- });
6143
- const {
6144
- // this token is needed to fetch conditions and flags,
6145
- // but it can't be used to record a session
6146
- token, userBrowser, userCity, userCountry, userDevice, userOS, userState, projectID, features, } = await r.json();
6147
- this.features = features ? features : this.features;
6148
- this.session.assign({ projectID });
6149
- this.session.setUserInfo({
6150
- userBrowser,
6151
- userCity,
6152
- userCountry,
6153
- userDevice,
6154
- userOS,
6155
- userState,
6156
- });
6157
- const onStartInfo = { sessionToken: token, userUUID: '', sessionID: '' };
6158
- this.startCallbacks.forEach((cb) => cb(onStartInfo));
6159
- await this.conditionsManager?.fetchConditions(projectID, token);
6160
- if (this.features['feature-flags']) {
6161
- await this.featureFlags.reloadFlags(token);
6162
- this.conditionsManager?.processFlags(this.featureFlags.flags);
6163
- }
6164
- await this.tagWatcher.fetchTags(this.options.ingestPoint, token);
6165
- }
6166
- /**
6167
- * Starts offline session recording
6168
- * @param {Object} startOpts - options for session start, same as .start()
6169
- * @param {Function} onSessionSent - callback that will be called once session is fully sent
6170
- * */
6171
- offlineRecording(startOpts = {}, onSessionSent) {
6172
- this.onSessionSent = onSessionSent;
6173
- this.singleBuffer = true;
6174
- const isNewSession = this.checkSessionToken(startOpts.forceNew);
6175
- adjustTimeOrigin();
6176
- this.coldStartTs = now();
6177
- const saverBuffer = this.localStorage.getItem(bufferStorageKey);
6178
- if (saverBuffer) {
6179
- const data = JSON.parse(saverBuffer);
6180
- this.bufferedMessages1 = Array.isArray(data) ? data : this.bufferedMessages1;
6181
- this.localStorage.removeItem(bufferStorageKey);
6182
- }
6183
- this.bufferedMessages1.push(Timestamp(this.timestamp()));
6184
- this.bufferedMessages1.push(TabData(this.session.getTabId()));
6185
- this.activityState = ActivityState.ColdStart;
6186
- if (startOpts.sessionHash) {
6187
- this.session.applySessionHash(startOpts.sessionHash);
6188
- }
6189
- if (startOpts.forceNew) {
6190
- this.session.reset();
6191
- }
6192
- this.session.assign({
6193
- userID: startOpts.userID,
6194
- metadata: startOpts.metadata,
6195
- });
6196
- const onStartInfo = { sessionToken: '', userUUID: '', sessionID: '' };
6197
- this.startCallbacks.forEach((cb) => cb(onStartInfo));
6198
- if (!isNewSession) {
6199
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
6200
- this.send(TabChange(this.session.getTabId()));
6201
- }
6202
- this.observer.observe();
6203
- this.ticker.start();
6204
- return {
6205
- saveBuffer: this.saveBuffer,
6206
- getBuffer: this.getBuffer,
6207
- setBuffer: this.setBuffer,
6208
- };
6209
- }
6210
- /**
6211
- * Saves the captured messages in localStorage (or whatever is used in its place)
6212
- *
6213
- * Then, when this.offlineRecording is called, it will preload this messages and clear the storage item
6214
- *
6215
- * Keeping the size of local storage reasonable is up to the end users of this library
6216
- * */
6217
- saveBuffer() {
6218
- this.localStorage.setItem(bufferStorageKey, JSON.stringify(this.bufferedMessages1));
6219
- }
6220
- /**
6221
- * @returns buffer with stored messages for offline recording
6222
- * */
6223
- getBuffer() {
6224
- return this.bufferedMessages1;
6225
- }
6226
- /**
6227
- * Used to set a buffer with messages array
6228
- * */
6229
- setBuffer(buffer) {
6230
- this.bufferedMessages1 = buffer;
6231
- }
6232
6233
  /**
6233
6234
  * Uploads the stored session buffer to backend
6234
6235
  * @returns promise that resolves once messages are loaded, it has to be awaited
@@ -9651,7 +9652,7 @@ class API {
9651
9652
  this.signalStartIssue = (reason, missingApi) => {
9652
9653
  const doNotTrack = this.checkDoNotTrack();
9653
9654
  console.log("Tracker couldn't start due to:", JSON.stringify({
9654
- trackerVersion: '16.4.2',
9655
+ trackerVersion: '16.4.3',
9655
9656
  projectKey: this.options.projectKey,
9656
9657
  doNotTrack,
9657
9658
  reason: missingApi.length ? `missing api: ${missingApi.join(',')}` : reason,