@openreplay/tracker 18.0.12-beta.1 → 18.0.13

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/index.js CHANGED
@@ -2344,8 +2344,9 @@ class Nodes {
2344
2344
  this.nextNodeId = this.createFrameId(level, frameOrder);
2345
2345
  }
2346
2346
  registerNode(node) {
2347
- let id = node[this.node_id];
2348
- const isNew = id === undefined;
2347
+ const existing = node[this.node_id];
2348
+ const isNew = existing === undefined || this.nodes.get(existing) !== node;
2349
+ let id = existing;
2349
2350
  if (isNew) {
2350
2351
  id = this.nextNodeId;
2351
2352
  this.totalNodeAmount++;
@@ -2374,6 +2375,12 @@ class Nodes {
2374
2375
  return undefined;
2375
2376
  return node[this.node_id];
2376
2377
  }
2378
+ isBound(node) {
2379
+ if (!node)
2380
+ return false;
2381
+ const id = node[this.node_id];
2382
+ return id !== undefined && this.nodes.get(id) === node;
2383
+ }
2377
2384
  getNode(id) {
2378
2385
  return this.nodes.get(id);
2379
2386
  }
@@ -2972,46 +2979,57 @@ class Observer {
2972
2979
  this.inlinerOptions = options.inlinerOptions;
2973
2980
  this.observer = createMutationObserver(this.app.safe((mutations) => {
2974
2981
  for (const mutation of mutations) {
2975
- // mutations order is sequential
2976
- const target = mutation.target;
2977
- const type = mutation.type;
2978
- if (!isObservable(target)) {
2979
- continue;
2980
- }
2981
- if (type === 'childList') {
2982
- for (let i = 0; i < mutation.removedNodes.length; i++) {
2983
- // Should be the same as bindTree(mutation.removedNodes[i]), but logic needs to be be untied
2984
- if (isObservable(mutation.removedNodes[i])) {
2985
- this.bindNode(mutation.removedNodes[i]);
2982
+ // THEORY S1: app.safe() wraps this whole callback in a try/catch that
2983
+ // SILENTLY swallows — so a throw while processing one mutation aborts
2984
+ // the entire batch (the single commitNodes() below never runs) and
2985
+ // every pending node in it is lost. Isolating + logging per mutation
2986
+ // both surfaces it into the session and prevents one bad node from
2987
+ // dropping the rest of the batch.
2988
+ try {
2989
+ // mutations order is sequential
2990
+ const target = mutation.target;
2991
+ const type = mutation.type;
2992
+ if (!isObservable(target)) {
2993
+ continue;
2994
+ }
2995
+ if (type === 'childList') {
2996
+ for (let i = 0; i < mutation.removedNodes.length; i++) {
2997
+ // Should be the same as bindTree(mutation.removedNodes[i]), but logic needs to be be untied
2998
+ if (isObservable(mutation.removedNodes[i])) {
2999
+ this.bindNode(mutation.removedNodes[i]);
3000
+ }
2986
3001
  }
3002
+ for (let i = 0; i < mutation.addedNodes.length; i++) {
3003
+ this.bindTree(mutation.addedNodes[i]);
3004
+ }
3005
+ continue;
2987
3006
  }
2988
- for (let i = 0; i < mutation.addedNodes.length; i++) {
2989
- this.bindTree(mutation.addedNodes[i]);
3007
+ const id = this.app.nodes.getID(target);
3008
+ if (id === undefined) {
3009
+ continue;
2990
3010
  }
2991
- continue;
2992
- }
2993
- const id = this.app.nodes.getID(target);
2994
- if (id === undefined) {
2995
- continue;
2996
- }
2997
- if (!this.recents.has(id)) {
2998
- this.recents.set(id, RecentsType.Changed); // TODO only when altered
2999
- }
3000
- if (type === 'attributes') {
3001
- const name = mutation.attributeName;
3002
- if (name === null) {
3011
+ if (!this.recents.has(id)) {
3012
+ this.recents.set(id, RecentsType.Changed); // TODO only when altered
3013
+ }
3014
+ if (type === 'attributes') {
3015
+ const name = mutation.attributeName;
3016
+ if (name === null) {
3017
+ continue;
3018
+ }
3019
+ let attr = this.attributesMap.get(id);
3020
+ if (attr === undefined) {
3021
+ this.attributesMap.set(id, (attr = new Set()));
3022
+ }
3023
+ attr.add(name);
3003
3024
  continue;
3004
3025
  }
3005
- let attr = this.attributesMap.get(id);
3006
- if (attr === undefined) {
3007
- this.attributesMap.set(id, (attr = new Set()));
3026
+ if (type === 'characterData') {
3027
+ this.textSet.add(id);
3028
+ continue;
3008
3029
  }
3009
- attr.add(name);
3010
- continue;
3011
3030
  }
3012
- if (type === 'characterData') {
3013
- this.textSet.add(id);
3014
- continue;
3031
+ catch (mutationErr) {
3032
+ mutation.target;
3015
3033
  }
3016
3034
  }
3017
3035
  this.commitNodes();
@@ -3177,10 +3195,11 @@ class Observer {
3177
3195
  this.bindNode(node);
3178
3196
  const walker = document.createTreeWalker(node, NodeFilter.SHOW_ELEMENT + NodeFilter.SHOW_TEXT, {
3179
3197
  acceptNode: (node) => {
3180
- if (this.app.nodes.getID(node) !== undefined) {
3181
- this.app.debug.info('! Node is already bound', node);
3182
- }
3183
- return isIgnored(node) || this.app.nodes.getID(node) !== undefined
3198
+ // Use the Map-aware isBound() rather than the raw __openreplay_id
3199
+ // property: a node carrying a stale id from a previous observe cycle
3200
+ // must be re-accepted and re-bound here, otherwise the snapshot walk
3201
+ // silently skips it (and its subtree, e.g. a <style>'s CSS text).
3202
+ return isIgnored(node) || this.app.nodes.isBound(node)
3184
3203
  ? NodeFilter.FILTER_REJECT
3185
3204
  : NodeFilter.FILTER_ACCEPT;
3186
3205
  },
@@ -3232,8 +3251,6 @@ class Observer {
3232
3251
  // TODO: Clean the logic (though now it workd fine)
3233
3252
  if (!hasTag(node, 'html') || !this.isTopContext) {
3234
3253
  if (parent === null) {
3235
- // Sometimes one observation contains attribute mutations for the removimg node, which gets ignored here.
3236
- // That shouldn't affect the visual rendering ( should it? maybe when transition applied? )
3237
3254
  this.unbindTree(node);
3238
3255
  return false;
3239
3256
  }
@@ -3387,6 +3404,26 @@ class Observer {
3387
3404
  this.commitNodes(true);
3388
3405
  }
3389
3406
  disconnect() {
3407
+ // THEORY S3: a disconnect may discard MutationRecords still queued by the
3408
+ // browser. takeRecords() drains them — they would be discarded by
3409
+ // disconnect() below anyway, so this changes nothing functionally, it only
3410
+ // lets us SEE whether a disconnect strands un-processed DOM additions
3411
+ // (e.g. a freshly appended <head> <style>). NOTE: if this disconnect is part
3412
+ // of stop(), the surrounding buffer may be cleared and this log lost; it is
3413
+ // most reliable for mid-session re-observes (cold-start cycle / iframe).
3414
+ const pending = this.observer.takeRecords();
3415
+ if (pending.length) {
3416
+ const tags = [];
3417
+ for (const m of pending) {
3418
+ for (let i = 0; i < m.addedNodes.length; i++) {
3419
+ const n = m.addedNodes[i];
3420
+ if (n.tagName) {
3421
+ if (tags.length < 10)
3422
+ tags.push(n.tagName);
3423
+ }
3424
+ }
3425
+ }
3426
+ }
3390
3427
  this.observer.disconnect();
3391
3428
  this.clear();
3392
3429
  this.throttledSetNodeData.clear();
@@ -4062,7 +4099,7 @@ class App {
4062
4099
  this.stopCallbacks = [];
4063
4100
  this.commitCallbacks = [];
4064
4101
  this.activityState = ActivityState.NotActive;
4065
- this.version = '18.0.12-beta.1'; // TODO: version compatability check inside each plugin.
4102
+ this.version = '18.0.13'; // TODO: version compatability check inside each plugin.
4066
4103
  this.socketMode = false;
4067
4104
  this.compressionThreshold = 24 * 1000;
4068
4105
  this.bc = null;
@@ -9360,7 +9397,7 @@ class ConstantProperties {
9360
9397
  user_id: this.user_id,
9361
9398
  distinct_id: this.deviceId,
9362
9399
  sdk_edition: 'web',
9363
- sdk_version: '18.0.12-beta.1',
9400
+ sdk_version: '18.0.13',
9364
9401
  timezone: getUTCOffsetString(),
9365
9402
  search_engine: this.searchEngine,
9366
9403
  };
@@ -10062,7 +10099,7 @@ class API {
10062
10099
  this.signalStartIssue = (reason, missingApi) => {
10063
10100
  const doNotTrack = this.checkDoNotTrack();
10064
10101
  console.log("Tracker couldn't start due to:", JSON.stringify({
10065
- trackerVersion: '18.0.12-beta.1',
10102
+ trackerVersion: '18.0.13',
10066
10103
  projectKey: this.options.projectKey,
10067
10104
  doNotTrack,
10068
10105
  reason: missingApi.length ? `missing api: ${missingApi.join(',')}` : reason,