@openreplay/tracker 17.2.8 → 17.2.9

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 CHANGED
@@ -2913,6 +2913,12 @@ class Observer {
2913
2913
  this.inlineRemoteCss = false;
2914
2914
  this.inlinerOptions = undefined;
2915
2915
  this.domParser = new DOMParser();
2916
+ /**
2917
+ * Bumped on every disconnect(). Stale async CSS-inlining callbacks from a
2918
+ * previous session (e.g. agent reload → tracker restart) compare against
2919
+ * this and bail instead of sending messages that reference vanished node ids.
2920
+ */
2921
+ this.generation = 0;
2916
2922
  this.throttling = true;
2917
2923
  this.throttledSetNodeData = throttleWithTrailing((id, parentElement, data) => this.sendNodeData(id, parentElement, data), 30);
2918
2924
  this.throttling = !Boolean(options.disableThrottling);
@@ -3059,14 +3065,21 @@ class Observer {
3059
3065
  }
3060
3066
  if (name === 'style' || (name === 'href' && hasTag(node, 'link'))) {
3061
3067
  if ('rel' in node && node.rel === 'stylesheet' && this.inlineRemoteCss) {
3068
+ const gen = this.generation;
3062
3069
  setTimeout(() => {
3063
3070
  inlineRemoteCss(
3064
3071
  // @ts-ignore
3065
3072
  node, id, this.app.getBaseHref(), nextID, (id, cssText, index, baseHref) => {
3073
+ if (this.generation !== gen)
3074
+ return;
3066
3075
  this.app.send(AdoptedSSInsertRuleURLBased(id, cssText, index, baseHref));
3067
3076
  }, (sheetId, ownerId) => {
3077
+ if (this.generation !== gen)
3078
+ return;
3068
3079
  this.app.send(AdoptedSSAddOwner(sheetId, ownerId));
3069
3080
  }, this.inlinerOptions?.forceFetch, this.inlinerOptions?.forcePlain, (cssText, fakeTextId) => {
3081
+ if (this.generation !== gen)
3082
+ return;
3070
3083
  this.app.send(CreateTextNode(fakeTextId, id, 0));
3071
3084
  this.app.send(SetCSSDataURLBased(fakeTextId, cssText, this.app.getBaseHref()));
3072
3085
  });
@@ -3332,6 +3345,7 @@ class Observer {
3332
3345
  this.observer.disconnect();
3333
3346
  this.clear();
3334
3347
  this.throttledSetNodeData.clear();
3348
+ this.generation++;
3335
3349
  }
3336
3350
  }
3337
3351
 
@@ -3995,7 +4009,7 @@ class App {
3995
4009
  this.stopCallbacks = [];
3996
4010
  this.commitCallbacks = [];
3997
4011
  this.activityState = ActivityState.NotActive;
3998
- this.version = '17.2.8'; // TODO: version compatability check inside each plugin.
4012
+ this.version = '17.2.9'; // TODO: version compatability check inside each plugin.
3999
4013
  this.socketMode = false;
4000
4014
  this.compressionThreshold = 24 * 1000;
4001
4015
  this.bc = null;
@@ -4017,9 +4031,13 @@ class App {
4017
4031
  if (!data || event.source === window)
4018
4032
  return;
4019
4033
  if (data.line === proto.startIframe) {
4020
- if (this.active())
4034
+ // Avoid corrupting an in-flight start; let it complete.
4035
+ if (this.activityState === ActivityState.Starting)
4021
4036
  return;
4022
4037
  try {
4038
+ if (this.active()) {
4039
+ this.stop();
4040
+ }
4023
4041
  if (data.token) {
4024
4042
  this.session.setSessionToken(data.token, this.projectKey);
4025
4043
  }
@@ -4051,17 +4069,22 @@ class App {
4051
4069
  }
4052
4070
  }
4053
4071
  };
4054
- /**
4055
- * context ids for iframes,
4056
- * order is not so important as long as its consistent
4057
- * */
4058
4072
  this.trackedFrames = [];
4073
+ this.frameLastSeen = new Map();
4074
+ this.FRAME_STALE_MS = 1500;
4059
4075
  this.crossDomainIframeListener = (event) => {
4060
- if (!this.active() || event.source === window)
4076
+ if (event.source === window)
4061
4077
  return;
4062
4078
  const { data } = event;
4063
4079
  if (!data)
4064
4080
  return;
4081
+ // Record liveness regardless of our own active state so the queue can prune
4082
+ // stale contexts reliably once we resume dispatching commands after a cycle.
4083
+ if ((data.line === proto.polling || data.line === proto.iframeSignal) && data.context) {
4084
+ this.frameLastSeen.set(data.context, Date.now());
4085
+ }
4086
+ if (!this.active())
4087
+ return;
4065
4088
  if (data.line === proto.iframeSignal) {
4066
4089
  // @ts-ignore
4067
4090
  event.source?.postMessage({ ping: true, line: proto.parentAlive }, '*');
@@ -4159,11 +4182,23 @@ class App {
4159
4182
  if (!this.pollingQueue.order.length) {
4160
4183
  return;
4161
4184
  }
4162
- const nextCommand = this.pollingQueue.order[0];
4163
- if (nextCommand && this.pollingQueue[nextCommand].length === 0) {
4164
- this.pollingQueue.order = this.pollingQueue.order.filter((c) => c !== nextCommand);
4185
+ this.pruneStaleFrames();
4186
+ const liveSet = new Set(this.trackedFrames);
4187
+ while (this.pollingQueue.order.length > 0) {
4188
+ const head = this.pollingQueue.order[0];
4189
+ this.pollingQueue[head] = this.pollingQueue[head].filter((ctx) => liveSet.has(ctx));
4190
+ if (this.pollingQueue[head].length === 0) {
4191
+ delete this.pollingQueue[head];
4192
+ this.pollingQueue.order.shift();
4193
+ }
4194
+ else {
4195
+ break;
4196
+ }
4197
+ }
4198
+ if (!this.pollingQueue.order.length) {
4165
4199
  return;
4166
4200
  }
4201
+ const nextCommand = this.pollingQueue.order[0];
4167
4202
  if (this.pollingQueue[nextCommand].includes(data.context)) {
4168
4203
  this.pollingQueue[nextCommand] = this.pollingQueue[nextCommand].filter((c) => c !== data.context);
4169
4204
  const message = { line: nextCommand };
@@ -4181,6 +4216,7 @@ class App {
4181
4216
  // @ts-ignore
4182
4217
  event.source?.postMessage(message, '*');
4183
4218
  if (this.pollingQueue[nextCommand].length === 0) {
4219
+ delete this.pollingQueue[nextCommand];
4184
4220
  this.pollingQueue.order.shift();
4185
4221
  }
4186
4222
  }
@@ -4194,6 +4230,11 @@ class App {
4194
4230
  order: [],
4195
4231
  };
4196
4232
  this.addCommand = (cmd) => {
4233
+ this.pruneStaleFrames();
4234
+ if (this.pollingQueue[cmd]) {
4235
+ this.pollingQueue[cmd] = Array.from(new Set([...this.pollingQueue[cmd], ...this.trackedFrames]));
4236
+ return;
4237
+ }
4197
4238
  this.pollingQueue.order.push(cmd);
4198
4239
  this.pollingQueue[cmd] = [...this.trackedFrames];
4199
4240
  };
@@ -4505,6 +4546,16 @@ class App {
4505
4546
  };
4506
4547
  }
4507
4548
  }
4549
+ pruneStaleFrames() {
4550
+ const staleAfter = Date.now() - this.FRAME_STALE_MS;
4551
+ this.trackedFrames = this.trackedFrames.filter((ctx) => {
4552
+ const last = this.frameLastSeen.get(ctx);
4553
+ if (last !== undefined && last >= staleAfter)
4554
+ return true;
4555
+ this.frameLastSeen.delete(ctx);
4556
+ return false;
4557
+ });
4558
+ }
4508
4559
  allowAppStart() {
4509
4560
  this.canStart = true;
4510
4561
  if (this.startTimeout) {
@@ -5055,6 +5106,9 @@ class App {
5055
5106
  if (Object.keys(startOpts).length !== 0) {
5056
5107
  this.prevOpts = startOpts;
5057
5108
  }
5109
+ if (startOpts.startCallback) {
5110
+ this.userStartCallback = startOpts.startCallback;
5111
+ }
5058
5112
  const isColdStart = this.activityState === ActivityState.ColdStart;
5059
5113
  if (isColdStart && this.coldInterval) {
5060
5114
  clearInterval(this.coldInterval);
@@ -5194,8 +5248,8 @@ class App {
5194
5248
  // TODO: start as early as possible (before receiving the token)
5195
5249
  /** after start */
5196
5250
  this.startCallbacks.forEach((cb) => cb(onStartInfo)); // MBTODO: callbacks after DOM "mounted" (observed)
5197
- if (startOpts.startCallback) {
5198
- startOpts.startCallback(SuccessfulStart(onStartInfo));
5251
+ if (this.userStartCallback) {
5252
+ this.userStartCallback(SuccessfulStart(onStartInfo));
5199
5253
  }
5200
5254
  this.activityState = ActivityState.Active;
5201
5255
  if (this.options.crossdomain?.enabled) {
@@ -5358,6 +5412,7 @@ class App {
5358
5412
  this.canvasRecorder?.clear();
5359
5413
  this.messages.length = 0;
5360
5414
  this.parentActive = false;
5415
+ this.pageFrames = [];
5361
5416
  }
5362
5417
  finally {
5363
5418
  this.activityState = ActivityState.NotActive;
@@ -8887,7 +8942,7 @@ class ConstantProperties {
8887
8942
  user_id: this.user_id,
8888
8943
  distinct_id: this.deviceId,
8889
8944
  sdk_edition: 'web',
8890
- sdk_version: '17.2.8',
8945
+ sdk_version: '17.2.9',
8891
8946
  timezone: getUTCOffsetString(),
8892
8947
  search_engine: this.searchEngine,
8893
8948
  };
@@ -9589,7 +9644,7 @@ class API {
9589
9644
  this.signalStartIssue = (reason, missingApi) => {
9590
9645
  const doNotTrack = this.checkDoNotTrack();
9591
9646
  console.log("Tracker couldn't start due to:", JSON.stringify({
9592
- trackerVersion: '17.2.8',
9647
+ trackerVersion: '17.2.9',
9593
9648
  projectKey: this.options.projectKey,
9594
9649
  doNotTrack,
9595
9650
  reason: missingApi.length ? `missing api: ${missingApi.join(',')}` : reason,